Statistics
| Revision:

root / src / summaryview.c @ 3281

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
static GdkPixbuf *html_pixbuf;
143

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

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

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

    
155
static void summary_update_msg_list        (SummaryView                *summaryview);
156

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

    
160
static void summary_set_menu_sensitive        (SummaryView                *summaryview);
161

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

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

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

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

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

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

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

    
203
static void summary_update_status        (SummaryView                *summaryview);
204

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

    
223
static void summary_activate_selected        (SummaryView                *summaryview);
224

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

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

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

    
250
static void summary_modify_threads        (SummaryView                *summaryview);
251

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
383

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

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

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

    
420
        N_DRAG_TYPES
421
};
422

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

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

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

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

    
510
        vbox = gtk_vbox_new(FALSE, 1);
511

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

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

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

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

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

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

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

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

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

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

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

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

    
599
        gtk_widget_show_all(vbox);
600

    
601
        return summaryview;
602
}
603

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

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

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

    
640
        pixmap = stock_pixbuf_widget(summaryview->hbox,
641
                                     STOCK_PIXMAP_FOLDER_OPEN);
642
        gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
643
        gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
644
        gtk_widget_show(pixmap);
645

    
646
        summary_clear_list(summaryview);
647
        summary_set_column_order(summaryview);
648
        summary_colorlabel_menu_create(summaryview);
649
        summary_set_menu_sensitive(summaryview);
650

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

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

    
670
        g_get_current_time(&tv_cur);
671

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

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

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

    
703
        if (summary_is_locked(summaryview)) return FALSE;
704

    
705
        inc_lock();
706
        summary_lock(summaryview);
707

    
708
        STATUSBAR_POP(summaryview->mainwin);
709

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

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

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

    
753
                summary_write_cache(summaryview);
754
        }
755

    
756
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item) !=
757
            FOLDER_ITEM_IS_SENT_FOLDER(item))
758
                set_column_order_required = TRUE;
759

    
760
        folderview_set_opened_item(summaryview->folderview, item);
761

    
762
        summary_clear_list_full(summaryview, is_refresh);
763

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

    
785
        if (!is_refresh)
786
                messageview_clear(summaryview->messageview);
787

    
788
        summaryview->folder_item = item;
789
        if (set_column_order_required)
790
                summary_set_column_order(summaryview);
791

    
792
        g_signal_handlers_block_matched(G_OBJECT(treeview),
793
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
794
                                        0, 0, NULL, NULL, summaryview);
795

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

    
801
        main_window_cursor_wait(summaryview->mainwin);
802

    
803
        save_data = item->folder->data;
804
        item->folder->data = summaryview;
805
        folder_set_ui_func(item->folder, get_msg_list_func, NULL);
806

    
807
        mlist = folder_item_get_msg_list(item, !update_cache);
808

    
809
        folder_set_ui_func(item->folder, NULL, NULL);
810
        item->folder->data = save_data;
811

    
812
        statusbar_pop_all();
813
        STATUSBAR_POP(summaryview->mainwin);
814

    
815
        summaryview->all_mlist = mlist;
816

    
817
        /* restore temporary move/copy marks */
818
        if (save_mark_mlist) {
819
                summary_restore_tmp_marks(summaryview, save_mark_mlist);
820
                save_mark_mlist = NULL;
821
        }
822

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

    
839
        if (do_qsearch) {
840
                gint index;
841
                QSearchCondType type = item->qsearch_cond_type;
842

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

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

    
874
        if (mlist)
875
                gtk_widget_grab_focus(GTK_WIDGET(treeview));
876

    
877
        summary_write_cache(summaryview);
878

    
879
        item->opened = TRUE;
880

    
881
        g_signal_handlers_unblock_matched(G_OBJECT(treeview),
882
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
883
                                          0, 0, NULL, NULL, summaryview);
884

    
885
        if (is_refresh) {
886
                summary_update_display_state(summaryview, displayed_msgnum,
887
                                             selected_msgnum);
888

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

    
916
                if (summaryview->selected)
917
                        selection_done = TRUE;
918
        }
919

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

    
953
        summary_status_show(summaryview);
954
        summary_set_menu_sensitive(summaryview);
955
        main_window_set_toolbar_sensitive(summaryview->mainwin);
956

    
957
        debug_print("\n");
958
        STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
959

    
960
        main_window_cursor_normal(summaryview->mainwin);
961

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

    
969
        summary_unlock(summaryview);
970
        inc_unlock();
971

    
972
        return TRUE;
973
}
974

    
975
static void summary_unset_sort_column_id(SummaryView *summaryview)
976
{
977
        gint id;
978
        GtkSortType order;
979

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

    
988
        gtkut_tree_sortable_unset_sort_column_id
989
                (GTK_TREE_SORTABLE(summaryview->store));
990
}
991

    
992
void summary_clear_list(SummaryView *summaryview)
993
{
994
        summary_clear_list_full(summaryview, FALSE);
995
}
996

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

    
1003
        if (summaryview->folder_item) {
1004
                folder_item_close(summaryview->folder_item);
1005
                summaryview->folder_item = NULL;
1006
        }
1007

    
1008
        summaryview->display_msg = FALSE;
1009

    
1010
        summaryview->selected = NULL;
1011
        summaryview->displayed = NULL;
1012

    
1013
        summary_selection_list_free(summaryview);
1014

    
1015
        summaryview->total_size = 0;
1016
        summaryview->deleted = summaryview->moved = 0;
1017
        summaryview->copied = 0;
1018

    
1019
        summary_msgid_table_destroy(summaryview);
1020

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

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

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

    
1059
        procmsg_msg_list_free(summaryview->all_mlist);
1060
        summaryview->all_mlist = NULL;
1061

    
1062
        gtkut_tree_view_fast_clear(treeview, summaryview->store);
1063

    
1064
        /* ensure that the "value-changed" signal is always emitted */
1065
        adj = gtk_tree_view_get_vadjustment(treeview);
1066
        adj->value = 0.0;
1067

    
1068
        summary_unset_sort_column_id(summaryview);
1069
}
1070

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

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

    
1088
        if (summary_is_locked(summaryview))
1089
                return;
1090

    
1091
        item = summaryview->folder_item;
1092
        if (!item || !item->path || !item->cache_queue ||
1093
            item->stype == F_VIRTUAL)
1094
                return;
1095

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

    
1098
        qlist = g_slist_reverse(item->cache_queue);
1099
        item->cache_queue = NULL;
1100
        if (item->mark_queue) {
1101
                procmsg_flaginfo_list_free(item->mark_queue);
1102
                item->mark_queue = NULL;
1103
        }
1104

    
1105
        for (cur = qlist; cur != NULL; cur = cur->next) {
1106
                msginfo = (MsgInfo *)cur->data;
1107

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

    
1114
                if (cur == qlist) {
1115
                        GtkTreePath *path;
1116

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

    
1125
                summaryview->total_size += msginfo->size;
1126
        }
1127

    
1128
        summaryview->all_mlist = g_slist_concat(summaryview->all_mlist, qlist);
1129

    
1130
        item->cache_dirty = TRUE;
1131
        summary_selection_list_free(summaryview);
1132

    
1133
        summary_status_show(summaryview);
1134

    
1135
        debug_print("summary_show_queued_msgs: done.\n");
1136
}
1137

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

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

    
1154
gboolean summary_is_locked(SummaryView *summaryview)
1155
{
1156
        return summaryview->lock_count > 0 || summaryview->write_lock_count > 0;
1157
}
1158

    
1159
gboolean summary_is_read_locked(SummaryView *summaryview)
1160
{
1161
        return summaryview->lock_count > 0;
1162
}
1163

    
1164
void summary_write_lock(SummaryView *summaryview)
1165
{
1166
        summaryview->write_lock_count++;
1167
}
1168

    
1169
void summary_write_unlock(SummaryView *summaryview)
1170
{
1171
        if (summaryview->write_lock_count)
1172
                summaryview->write_lock_count--;
1173
}
1174

    
1175
gboolean summary_is_write_locked(SummaryView *summaryview)
1176
{
1177
        return summaryview->write_lock_count > 0;
1178
}
1179

    
1180
FolderItem *summary_get_current_folder(SummaryView *summaryview)
1181
{
1182
        return summaryview->folder_item;
1183
}
1184

    
1185
SummarySelection summary_get_selection_type(SummaryView *summaryview)
1186
{
1187
        SummarySelection selection;
1188
        GList *rows;
1189

    
1190
        rows = summary_get_selected_rows(summaryview);
1191

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

    
1201
        return selection;
1202
}
1203

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

    
1211
        return summaryview->selection_list;
1212
}
1213

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

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

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

    
1240
        mlist = g_slist_reverse(mlist);
1241

    
1242
        return mlist;
1243
}
1244

    
1245
GSList *summary_get_changed_msg_list(SummaryView *summaryview)
1246
{
1247
        MsgInfo *msginfo;
1248
        GSList *mlist = NULL;
1249
        GSList *cur;
1250

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

    
1257
        return g_slist_reverse(mlist);
1258
}
1259

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

    
1268
GSList *summary_get_flagged_msg_list(SummaryView *summaryview,
1269
                                     MsgPermFlags flags)
1270
{
1271
        MsgInfo *msginfo;
1272
        GSList *list, *cur;
1273
        GSList *mlist = NULL;
1274

    
1275
        if (summaryview->on_filter)
1276
                list = summaryview->flt_mlist;
1277
        else
1278
                list = summaryview->all_mlist;
1279

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

    
1286
        return g_slist_reverse(mlist);
1287
}
1288

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

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

    
1309
        return g_slist_reverse(mlist);
1310
}
1311

    
1312
static void summary_restore_tmp_marks(SummaryView *summaryview,
1313
                                      GSList *save_mark_mlist)
1314
{
1315
        GSList *cur, *scur;
1316
        MsgInfo *msginfo, *markinfo;
1317

    
1318
        debug_print("summary_restore_tmp_marks: restoring temporary marks\n");
1319

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

    
1338
        if (save_mark_mlist)
1339
                procmsg_msg_list_free(save_mark_mlist);
1340
}
1341

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

    
1350
        if (summaryview->on_filter)
1351
                return;
1352

    
1353
        g_slist_free(summaryview->all_mlist);
1354
        summaryview->all_mlist = NULL;
1355

    
1356
        valid = gtk_tree_model_get_iter_first(model, &iter);
1357

    
1358
        while (valid) {
1359
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1360
                mlist = g_slist_prepend(mlist, msginfo);
1361
                valid = gtkut_tree_model_next(model, &iter);
1362
        }
1363

    
1364
        summaryview->all_mlist = g_slist_reverse(mlist);
1365

    
1366
}
1367

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

    
1377
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
1378

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

    
1386
        return FALSE;
1387
}
1388

    
1389
static void summary_msgid_table_create(SummaryView *summaryview)
1390
{
1391
        GHashTable *msgid_table;
1392

    
1393
        g_return_if_fail(summaryview->msgid_table == NULL);
1394

    
1395
        msgid_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1396
                                            (GDestroyNotify)gtk_tree_iter_free);
1397

    
1398
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
1399
                               summary_msgid_table_create_func, msgid_table);
1400

    
1401
        summaryview->msgid_table = msgid_table;
1402
}
1403

    
1404
static void summary_msgid_table_destroy(SummaryView *summaryview)
1405
{
1406
        if (!summaryview->msgid_table)
1407
                return;
1408

    
1409
        g_hash_table_destroy(summaryview->msgid_table);
1410
        summaryview->msgid_table = NULL;
1411
}
1412

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

    
1420
        selection = summary_get_selection_type(summaryview);
1421
        sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1422

    
1423
        main_window_set_menu_sensitive(summaryview->mainwin);
1424

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

    
1435
        if (selection == SUMMARY_NONE) {
1436
                menu_set_insensitive_all
1437
                        (GTK_MENU_SHELL(summaryview->popupmenu));
1438
                return;
1439
        }
1440

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

    
1450
        menu_set_sensitive(ifactory, "/Copy...", TRUE);
1451

    
1452
        menu_set_sensitive(ifactory, "/Mark", TRUE);
1453
        menu_set_sensitive(ifactory, "/Mark/Set flag",   TRUE);
1454
        menu_set_sensitive(ifactory, "/Mark/Unset flag", TRUE);
1455

    
1456
        menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1457
        menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
1458
        menu_set_sensitive(ifactory, "/Mark/Mark all read",  TRUE);
1459

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

    
1474
        menu_set_sensitive(ifactory, "/Color label", TRUE);
1475

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

    
1485
        menu_set_sensitive(ifactory, "/Add sender to address book...", sens);
1486
        menu_set_sensitive(ifactory, "/Create filter rule", sens);
1487

    
1488
        menu_set_sensitive(ifactory, "/View", sens);
1489
        menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1490
        menu_set_sensitive(ifactory, "/View/Message source", sens);
1491
        menu_set_sensitive(ifactory, "/View/All headers", sens);
1492

    
1493
        menu_set_sensitive(ifactory, "/Print...",   TRUE);
1494

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

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

    
1514
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1515
                                               &iter))
1516
                return;
1517

    
1518
        if (!messageview_is_visible(summaryview->messageview) ||
1519
            summary_row_is_displayed(summaryview, &iter))
1520
                start_from_prev = TRUE;
1521

    
1522
        found = summary_find_prev_flagged_msg
1523
                (summaryview, &prev, &iter, flags, start_from_prev);
1524

    
1525
        if (!found) {
1526
                AlertValue val;
1527

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

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

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

    
1558
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1559
                                               &iter)) {
1560
                if (!gtk_tree_model_get_iter_first(model, &iter))
1561
                        return;
1562
        }
1563

    
1564
        if (!messageview_is_visible(summaryview->messageview) ||
1565
            summary_row_is_displayed(summaryview, &iter))
1566
                start_from_next = TRUE;
1567

    
1568
        found = summary_find_next_flagged_msg
1569
                (summaryview, &next, &iter, flags, start_from_next);
1570

    
1571
        if (!found) {
1572
                AlertValue val;
1573

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

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

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

    
1604
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1605
                                               &iter)) {
1606
                if (!gtk_tree_model_get_iter_first(model, &iter))
1607
                        return;
1608
        }
1609

    
1610
        if (!messageview_is_visible(summaryview->messageview) ||
1611
            summary_row_is_displayed(summaryview, &iter))
1612
                start_from_next = TRUE;
1613

    
1614
        while (summary_find_next_flagged_msg
1615
                (summaryview, &next, &iter, flags, start_from_next) == FALSE) {
1616
                AlertValue val;
1617

    
1618
                val = alertpanel(title, ask_msg,
1619
                                 GTK_STOCK_YES, GTK_STOCK_NO,
1620
                                 _("_Search again"));
1621

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

    
1633
        visible = messageview_is_visible(summaryview->messageview);
1634
        summary_select_row(summaryview, &next, visible, FALSE);
1635
        if (visible)
1636
                summary_mark_displayed_read(summaryview, &next);
1637
}
1638

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

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

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

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

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

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

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

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

    
1711
void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1712
{
1713
        GtkTreeIter iter;
1714

    
1715
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1716
                summary_select_row(summaryview, &iter, FALSE, TRUE);
1717
}
1718

    
1719
gboolean summary_select_by_msginfo(SummaryView *summaryview, MsgInfo *msginfo)
1720
{
1721
        GtkTreeIter iter;
1722

    
1723
        if (summaryview->folder_item != msginfo->folder)
1724
                return FALSE;
1725

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

    
1733
        return FALSE;
1734
}
1735

    
1736
MsgInfo *summary_get_msginfo_by_msgnum(SummaryView *summaryview, guint msgnum)
1737
{
1738
        GtkTreeIter iter;
1739
        MsgInfo *msginfo = NULL;
1740

    
1741
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1742
                GET_MSG_INFO(msginfo, &iter);
1743

    
1744
        return msginfo;
1745
}
1746

    
1747
static gboolean summary_select_true_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean cur_selected, gpointer data)
1748
{
1749
        return TRUE;
1750
}
1751

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

    
1769
        if (!iter)
1770
                return;
1771

    
1772
        gtkut_tree_view_expand_parent_all
1773
                (GTK_TREE_VIEW(summaryview->treeview), iter);
1774

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

    
1799
        gtk_tree_path_free(path);
1800
}
1801

    
1802
static void summary_scroll_to_selected(SummaryView *summaryview,
1803
                                       gboolean align_center)
1804
{
1805
        GtkTreePath *path;
1806

    
1807
        if (!summaryview->selected)
1808
                return;
1809

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

    
1824
static MsgInfo *summary_get_msginfo(SummaryView *summaryview,
1825
                                    GtkTreeRowReference *row)
1826
{
1827
        GtkTreeIter iter;
1828
        MsgInfo *msginfo = NULL;
1829

    
1830
        if (!row)
1831
                return 0;
1832
        if (!gtkut_tree_row_reference_get_iter
1833
                (GTK_TREE_MODEL(summaryview->store), row, &iter))
1834
                return 0;
1835

    
1836
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), &iter,
1837
                           S_COL_MSG_INFO, &msginfo, -1);
1838

    
1839
        return msginfo;
1840
}
1841

    
1842
static guint summary_get_msgnum(SummaryView *summaryview,
1843
                                GtkTreeRowReference *row)
1844
{
1845
        MsgInfo *msginfo;
1846

    
1847
        msginfo = summary_get_msginfo(summaryview, row);
1848
        if (!msginfo)
1849
                return 0;
1850

    
1851
        return msginfo->msgnum;
1852
}
1853

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

    
1861
        if (!iter)
1862
                return FALSE;
1863

    
1864
        iter_ = *iter;
1865

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

    
1877
        return FALSE;
1878
}
1879

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

    
1887
        if (!iter)
1888
                return FALSE;
1889

    
1890
        iter_ = *iter;
1891

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

    
1903
        return FALSE;
1904
}
1905

    
1906
static gboolean summary_find_nearest_msg(SummaryView *summaryview,
1907
                                         GtkTreeIter *target, GtkTreeIter *iter)
1908
{
1909
        gboolean valid;
1910

    
1911
        valid = summary_find_next_msg(summaryview, target, iter);
1912
        if (!valid)
1913
                valid = summary_find_prev_msg(summaryview, target, iter);
1914

    
1915
        return valid;
1916
}
1917

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

    
1929
        if (iter) {
1930
                iter_ = *iter;
1931
                if (start_from_prev)
1932
                        valid = gtkut_tree_model_prev(model, &iter_);
1933
        } else
1934
                valid = gtkut_tree_model_get_iter_last(model, &iter_);
1935

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

    
1944
        return FALSE;
1945
}
1946

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

    
1958
        if (iter) {
1959
                iter_ = *iter;
1960
                if (start_from_next)
1961
                        valid = gtkut_tree_model_next(model, &iter_);
1962
        } else
1963
                valid = gtk_tree_model_get_iter_first(model, &iter_);
1964

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

    
1973
        return FALSE;
1974
}
1975

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

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

    
1993
        return FALSE;
1994
}
1995

    
1996
static void summary_update_display_state(SummaryView *summaryview,
1997
                                         guint disp_msgnum, guint sel_msgnum)
1998
{
1999
        GtkTreeIter iter;
2000

    
2001
        if (summary_find_msg_by_msgnum(summaryview, disp_msgnum, &iter)) {
2002
                GtkTreePath *path;
2003
                GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2004

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

    
2013
        summary_select_by_msgnum(summaryview, sel_msgnum);
2014
}
2015

    
2016
static guint attract_hash_func(gconstpointer key)
2017
{
2018
        gchar str[BUFFSIZE];
2019
        gchar *p;
2020
        guint h;
2021

    
2022
        strncpy2(str, (const gchar *)key, sizeof(str));
2023
        trim_subject_for_compare(str);
2024

    
2025
        p = str;
2026
        h = *p;
2027

    
2028
        if (h) {
2029
                for (p += 1; *p != '\0'; p++)
2030
                        h = (h << 5) - h + *p;
2031
        }
2032

    
2033
        return h;
2034
}
2035

    
2036
static gint attract_compare_func(gconstpointer a, gconstpointer b)
2037
{
2038
        return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2039
}
2040

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

    
2052
        if (!summaryview->folder_item)
2053
                return;
2054
        if (summaryview->folder_item->sort_key != SORT_BY_NONE)
2055
                return;
2056

    
2057
        valid = gtk_tree_model_get_iter_first(model, &iter);
2058
        if (!valid)
2059
                return;
2060

    
2061
        debug_print("Attracting messages by subject...");
2062
        STATUSBAR_PUSH(summaryview->mainwin,
2063
                       _("Attracting messages by subject..."));
2064

    
2065
        main_window_cursor_wait(summaryview->mainwin);
2066

    
2067
        order_table = g_hash_table_new(NULL, NULL);
2068

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

    
2078
        mlist = g_slist_reverse(mlist);
2079

    
2080
        subject_table = g_hash_table_new(attract_hash_func,
2081
                                         attract_compare_func);
2082

    
2083
        for (list = mlist; list != NULL; list = next) {
2084
                msginfo = (MsgInfo *)list->data;
2085

    
2086
                next = list->next;
2087

    
2088
                if (!msginfo->subject) {
2089
                        last = list;
2090
                        continue;
2091
                }
2092

    
2093
                /* find attracting node */
2094
                dest = g_hash_table_lookup(subject_table, msginfo->subject);
2095

    
2096
                if (dest) {
2097
                        dest_msginfo = (MsgInfo *)dest->data;
2098

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

    
2107
                        if (dest->next != list) {
2108
                                last->next = list->next;
2109
                                list->next = dest->next;
2110
                                dest->next = list;
2111
                        } else
2112
                                last = list;
2113
                } else
2114
                        last = list;
2115

    
2116
                g_hash_table_replace(subject_table, msginfo->subject, list);
2117
        }
2118

    
2119
        g_hash_table_destroy(subject_table);
2120

    
2121
        new_order = g_new(gint, count);
2122
        for (list = mlist, i = 0; list != NULL; list = list->next, ++i) {
2123
                gint old_pos;
2124

    
2125
                msginfo = (MsgInfo *)list->data;
2126

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

    
2134
        g_slist_free(mlist);
2135
        g_hash_table_destroy(order_table);
2136

    
2137
        summaryview->folder_item->cache_dirty = TRUE;
2138
        summary_selection_list_free(summaryview);
2139
        summary_update_msg_list(summaryview);
2140

    
2141
        summary_scroll_to_selected(summaryview, TRUE);
2142

    
2143
        debug_print("done.\n");
2144
        STATUSBAR_POP(summaryview->mainwin);
2145

    
2146
        main_window_cursor_normal(summaryview->mainwin);
2147
}
2148

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

    
2159
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2160
                msginfo = (MsgInfo *)cur->data;
2161

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

    
2171
        for (cur = summaryview->flt_mlist; cur != NULL; cur = cur->next) {
2172
                msginfo = (MsgInfo *)cur->data;
2173

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

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

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

    
2214
        if (!summaryview->folder_item) {
2215
                gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
2216
                gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
2217
                gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
2218
                return;
2219
        }
2220

    
2221
        rowlist = summary_get_selected_rows(summaryview);
2222
        for (cur = rowlist; cur != NULL; cur = cur->next) {
2223
                GtkTreeIter iter;
2224
                GtkTreePath *path = (GtkTreePath *)cur->data;
2225

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

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

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

    
2256
        str = g_string_sized_new(128);
2257

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

    
2273
        gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str->str);
2274
        g_string_truncate(str, 0);
2275

    
2276
        new = summaryview->folder_item->new;
2277
        unread = summaryview->folder_item->unread;
2278
        total = summaryview->folder_item->total;
2279
        total_size = summaryview->total_size;
2280

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

    
2286
                f_new = summaryview->flt_new;
2287
                f_unread = summaryview->flt_unread;
2288
                f_total = summaryview->flt_msg_total;
2289
                f_total_size = summaryview->total_flt_msg_size;
2290

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

    
2309
        gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str->str);
2310
        g_string_free(str, TRUE);
2311

    
2312
        folderview_update_opened_msg_num(summaryview->folderview);
2313
}
2314

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

    
2323
        g_return_if_fail(sort_key >= SORT_BY_NONE && sort_key <= SORT_BY_TO);
2324

    
2325
        if (!item || !item->path || !item->parent || item->no_select) return;
2326

    
2327
        if (item->sort_key != sort_key || item->sort_type != sort_type)
2328
                item->cache_dirty = TRUE;
2329

    
2330
        col_type = sort_key_to_col[sort_key];
2331
        prev_col_type = sort_key_to_col[item->sort_key];
2332

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

    
2341
        debug_print("Sorting summary by key: %d...\n", sort_key);
2342
        STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
2343

    
2344
        main_window_cursor_wait(summaryview->mainwin);
2345

    
2346
        item->sort_key = sort_key;
2347
        item->sort_type = sort_type;
2348

    
2349
        gtk_tree_sortable_set_sort_column_id(sortable, col_type,
2350
                                             (GtkSortType)sort_type);
2351

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

    
2367
        summary_selection_list_free(summaryview);
2368
        if (summaryview->all_mlist)
2369
                summary_update_msg_list(summaryview);
2370

    
2371
        summary_set_menu_sensitive(summaryview);
2372

    
2373
        summary_scroll_to_selected(summaryview, TRUE);
2374

    
2375
        debug_print("done.\n");
2376
        STATUSBAR_POP(summaryview->mainwin);
2377

    
2378
        main_window_cursor_normal(summaryview->mainwin);
2379
}
2380

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

    
2389
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
2390
        if (MSG_IS_UNREAD(msginfo->flags))
2391
                return TRUE;
2392

    
2393
        valid = gtk_tree_model_iter_children(model, &iter_, iter);
2394

    
2395
        while (valid) {
2396
                if (summary_have_unread_children(summaryview, &iter_))
2397
                        return TRUE;
2398

    
2399
                valid = gtk_tree_model_iter_next(model, &iter_);
2400
        }
2401

    
2402
        return FALSE;
2403
}
2404

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

    
2423
        if (!msginfo) {
2424
                GET_MSG_INFO(msginfo, iter);
2425
        }
2426

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

    
2439
                strncpy2(from, msginfo->from, sizeof(from));
2440
                extract_address(from);
2441
                if (account_address_exist(from))
2442
                        sw_from_s = g_strconcat("-->", msginfo->to, NULL);
2443
        }
2444

    
2445
        if (msginfo->subject && *msginfo->subject) {
2446
                if (msginfo->folder && msginfo->folder->trim_summary_subject) {
2447
                        subject_s = g_strdup(msginfo->subject);
2448
                        trim_subject(subject_s);
2449
                }
2450
        }
2451

    
2452
        if (msginfo->to)
2453
                to_s = procheader_get_toname(msginfo->to);
2454

    
2455
        flags = msginfo->flags;
2456

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

    
2470
        if (MSG_IS_NEW(flags))
2471
                unread_pix = new_pixbuf;
2472
        else if (MSG_IS_UNREAD(flags))
2473
                unread_pix = unread_pixbuf;
2474
        else if (MSG_IS_REPLIED(flags))
2475
                unread_pix = replied_pixbuf;
2476
        else if (MSG_IS_FORWARDED(flags))
2477
                unread_pix = forwarded_pixbuf;
2478

    
2479
        if (MSG_IS_MIME(flags)) {
2480
                mime_pix = clip_pixbuf;
2481
        }
2482
        if (MSG_IS_MIME_HTML(flags)) {
2483
                mime_pix = html_pixbuf;
2484
        }
2485

    
2486
        if (prefs_common.bold_unread) {
2487
                if (MSG_IS_UNREAD(flags))
2488
                        weight = PANGO_WEIGHT_BOLD;
2489
                else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store),
2490
                                                       iter)) {
2491
                        GtkTreePath *path;
2492

    
2493
                        path = gtk_tree_model_get_path
2494
                                (GTK_TREE_MODEL(store), iter);
2495
                        if (!gtk_tree_view_row_expanded
2496
                                (GTK_TREE_VIEW(summaryview->treeview), path) &&
2497
                            summary_have_unread_children(summaryview, iter))
2498
                                weight = PANGO_WEIGHT_BOLD;
2499
                        gtk_tree_path_free(path);
2500
                }
2501
        }
2502

    
2503
        color_val = MSG_GET_COLORLABEL_VALUE(flags);
2504
        if (color_val != 0) {
2505
                color = colorlabel_get_color(color_val - 1);
2506
                foreground = &color;
2507
        }
2508

    
2509
        gtk_tree_store_set(store, iter,
2510
                           S_COL_MARK, mark_pix,
2511
                           S_COL_UNREAD, unread_pix,
2512
                           S_COL_MIME, mime_pix,
2513
                           S_COL_SUBJECT, subject_s ? subject_s :
2514
                                             msginfo->subject && *msginfo->subject ? msginfo->subject :
2515
                                          _("(No Subject)"),
2516
                           S_COL_FROM, sw_from_s ? sw_from_s :
2517
                                          msginfo->fromname ? msginfo->fromname :
2518
                                       _("(No From)"),
2519
                           S_COL_DATE, date_s,
2520
                           S_COL_SIZE, to_human_readable(msginfo->size),
2521
                           S_COL_NUMBER, msginfo->msgnum,
2522
                           S_COL_TO, to_s ? to_s : "",
2523

    
2524
                           S_COL_MSG_INFO, msginfo,
2525

    
2526
                           S_COL_LABEL, color_val,
2527

    
2528
                           S_COL_FOREGROUND, foreground,
2529
                           S_COL_BOLD, weight,
2530
                           -1);
2531

    
2532
        if (to_s)
2533
                g_free(to_s);
2534
        if (subject_s)
2535
                g_free(subject_s);
2536
        if (sw_from_s)
2537
                g_free(sw_from_s);
2538
}
2539

    
2540
static void summary_insert_gnode(SummaryView *summaryview, GtkTreeStore *store,
2541
                                 GtkTreeIter *iter, GtkTreeIter *parent,
2542
                                 GtkTreeIter *sibling, GNode *gnode)
2543
{
2544
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2545

    
2546
        if (parent && !sibling)
2547
                gtk_tree_store_append(store, iter, parent);
2548
        else
2549
                gtk_tree_store_insert_after(store, iter, parent, sibling);
2550

    
2551
        summary_set_row(summaryview, iter, msginfo);
2552

    
2553
        if (!parent) {
2554
                guint tdate;
2555

    
2556
                tdate = procmsg_get_thread_date(gnode);
2557
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2558
        }
2559

    
2560
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2561
                GtkTreeIter child;
2562

    
2563
                summary_insert_gnode(summaryview, store, &child, iter, NULL,
2564
                                     gnode);
2565
        }
2566
}
2567

    
2568
static void summary_insert_gnode_before(SummaryView *summaryview,
2569
                                        GtkTreeStore *store,
2570
                                        GtkTreeIter *iter, GtkTreeIter *parent,
2571
                                        GtkTreeIter *sibling, GNode *gnode)
2572
{
2573
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2574

    
2575
        gtk_tree_store_insert_before(store, iter, parent, sibling);
2576

    
2577
        summary_set_row(summaryview, iter, msginfo);
2578

    
2579
        if (!parent) {
2580
                guint tdate;
2581

    
2582
                tdate = procmsg_get_thread_date(gnode);
2583
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2584
        }
2585

    
2586
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2587
                GtkTreeIter child;
2588

    
2589
                summary_insert_gnode_before(summaryview, store, &child, iter,
2590
                                            NULL, gnode);
2591
        }
2592
}
2593

    
2594
static void summary_set_tree_model_from_list(SummaryView *summaryview,
2595
                                             GSList *mlist)
2596
{
2597
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2598
        GtkTreeIter iter;
2599
        MsgInfo *msginfo;
2600
        GSList *cur;
2601

    
2602
        debug_print(_("\tSetting summary from message data..."));
2603
        STATUSBAR_PUSH(summaryview->mainwin,
2604
                       _("Setting summary from message data..."));
2605
        gdk_flush();
2606

    
2607
        /* temporarily remove the model for speed up */
2608
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL);
2609

    
2610
        if (summaryview->folder_item->threaded) {
2611
                GNode *root, *gnode;
2612

    
2613
                root = procmsg_get_thread_tree(mlist);
2614

    
2615
                for (gnode = root->children; gnode != NULL;
2616
                     gnode = gnode->next) {
2617
                        summary_insert_gnode
2618
                                (summaryview, store, &iter, NULL, NULL, gnode);
2619
                        if (gnode->children && !prefs_common.expand_thread &&
2620
                            prefs_common.bold_unread &&
2621
                            summary_have_unread_children(summaryview, &iter)) {
2622
                                gtk_tree_store_set(store, &iter,
2623
                                                   S_COL_BOLD,
2624
                                                   PANGO_WEIGHT_BOLD, -1);
2625
                        }
2626
                }
2627

    
2628
                g_node_destroy(root);
2629

    
2630
                for (cur = mlist; cur != NULL; cur = cur->next) {
2631
                        msginfo = (MsgInfo *)cur->data;
2632

    
2633
                        if (MSG_IS_DELETED(msginfo->flags))
2634
                                summaryview->deleted++;
2635
                        if (MSG_IS_MOVE(msginfo->flags))
2636
                                summaryview->moved++;
2637
                        if (MSG_IS_COPY(msginfo->flags))
2638
                                summaryview->copied++;
2639
                        summaryview->total_size += msginfo->size;
2640
                }
2641
        } else {
2642
                GSList *rev_mlist;
2643
                GtkTreeIter iter;
2644

    
2645
                rev_mlist = g_slist_reverse(g_slist_copy(mlist));
2646
                for (cur = rev_mlist; cur != NULL; cur = cur->next) {
2647
                        msginfo = (MsgInfo *)cur->data;
2648

    
2649
                        gtk_tree_store_prepend(store, &iter, NULL);
2650
                        summary_set_row(summaryview, &iter, msginfo);
2651

    
2652
                        if (MSG_IS_DELETED(msginfo->flags))
2653
                                summaryview->deleted++;
2654
                        if (MSG_IS_MOVE(msginfo->flags))
2655
                                summaryview->moved++;
2656
                        if (MSG_IS_COPY(msginfo->flags))
2657
                                summaryview->copied++;
2658
                        summaryview->total_size += msginfo->size;
2659
                }
2660
                g_slist_free(rev_mlist);
2661
        }
2662

    
2663
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview),
2664
                                GTK_TREE_MODEL(store));
2665

    
2666
        if (summaryview->folder_item->threaded && prefs_common.expand_thread)
2667
                gtk_tree_view_expand_all
2668
                        (GTK_TREE_VIEW(summaryview->treeview));
2669

    
2670
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
2671
                summary_sort(summaryview, summaryview->folder_item->sort_key,
2672
                             summaryview->folder_item->sort_type);
2673
        }
2674

    
2675
        debug_print(_("done.\n"));
2676
        STATUSBAR_POP(summaryview->mainwin);
2677
}
2678

    
2679
struct wcachefp
2680
{
2681
        FILE *cache_fp;
2682
        FILE *mark_fp;
2683
};
2684

    
2685
gint summary_write_cache(SummaryView *summaryview)
2686
{
2687
        struct wcachefp fps;
2688
        FolderItem *item;
2689
        gchar *buf;
2690
        GSList *cur;
2691

    
2692
        item = summaryview->folder_item;
2693
        if (!item || !item->path)
2694
                return -1;
2695
        if (item->mark_queue)
2696
                item->mark_dirty = TRUE;
2697
        if (!item->cache_dirty && !item->mark_dirty)
2698
                return 0;
2699

    
2700
        if (item->cache_dirty) {
2701
                fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE);
2702
                if (fps.cache_fp == NULL)
2703
                        return -1;
2704
                item->mark_dirty = TRUE;
2705
        } else
2706
                fps.cache_fp = NULL;
2707

    
2708
        if (item->mark_dirty && item->stype != F_VIRTUAL) {
2709
                fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE);
2710
                if (fps.mark_fp == NULL) {
2711
                        if (fps.cache_fp)
2712
                                fclose(fps.cache_fp);
2713
                        return -1;
2714
                }
2715
        } else
2716
                fps.mark_fp = NULL;
2717

    
2718
        if (item->cache_dirty) {
2719
                buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2720
                                      item->path);
2721
                debug_print("%s", buf);
2722
                STATUSBAR_PUSH(summaryview->mainwin, buf);
2723
                gdk_flush();
2724
                g_free(buf);
2725
        }
2726

    
2727
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2728
                MsgInfo *msginfo = (MsgInfo *)cur->data;
2729

    
2730
                if (msginfo->folder && msginfo->folder->mark_queue != NULL) {
2731
                        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
2732
                }
2733
                if (fps.cache_fp)
2734
                        procmsg_write_cache(msginfo, fps.cache_fp);
2735
                if (fps.mark_fp)
2736
                        procmsg_write_flags(msginfo, fps.mark_fp);
2737
        }
2738

    
2739
        if (item->cache_queue)
2740
                procmsg_flush_cache_queue(item, fps.cache_fp);
2741
        if (item->mark_queue)
2742
                procmsg_flush_mark_queue(item, fps.mark_fp);
2743

    
2744
        item->unmarked_num = 0;
2745

    
2746
        if (fps.cache_fp)
2747
                fclose(fps.cache_fp);
2748
        if (fps.mark_fp)
2749
                fclose(fps.mark_fp);
2750

    
2751
        if (item->stype == F_VIRTUAL) {
2752
                GSList *mlist;
2753

    
2754
                mlist = summary_get_changed_msg_list(summaryview);
2755
                if (mlist) {
2756
                        procmsg_write_flags_for_multiple_folders(mlist);
2757
                        g_slist_free(mlist);
2758
                        folderview_update_all_updated(FALSE);
2759
                }
2760
        }
2761

    
2762
        debug_print(_("done.\n"));
2763

    
2764
        if (item->cache_dirty) {
2765
                STATUSBAR_POP(summaryview->mainwin);
2766
        }
2767

    
2768
        item->cache_dirty = item->mark_dirty = FALSE;
2769

    
2770
        return 0;
2771
}
2772

    
2773
static gboolean summary_row_is_displayed(SummaryView *summaryview,
2774
                                         GtkTreeIter *iter)
2775
{
2776
        GtkTreePath *disp_path, *path;
2777
        gint ret;
2778

    
2779
        if (!summaryview->displayed || !iter)
2780
                return FALSE;
2781

    
2782
        disp_path = gtk_tree_row_reference_get_path(summaryview->displayed);
2783
        if (!disp_path)
2784
                return FALSE;
2785

    
2786
        path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
2787
                                       iter);
2788
        if (!path) {
2789
                gtk_tree_path_free(disp_path);
2790
                return FALSE;
2791
        }
2792

    
2793
        ret = gtk_tree_path_compare(disp_path, path);
2794
        gtk_tree_path_free(path);
2795
        gtk_tree_path_free(disp_path);
2796

    
2797
        return (ret == 0);
2798
}
2799

    
2800
static void summary_display_msg(SummaryView *summaryview, GtkTreeIter *iter)
2801
{
2802
        summary_display_msg_full(summaryview, iter, FALSE, FALSE, FALSE);
2803
}
2804

    
2805
static void summary_display_msg_full(SummaryView *summaryview,
2806
                                     GtkTreeIter *iter,
2807
                                     gboolean new_window, gboolean all_headers,
2808
                                     gboolean redisplay)
2809
{
2810
        GtkTreePath *path;
2811
        MsgInfo *msginfo = NULL;
2812
        gint val;
2813
        gboolean do_mark_read = FALSE;
2814

    
2815
        g_return_if_fail(iter != NULL);
2816

    
2817
        if (!new_window && !redisplay &&
2818
            summary_row_is_displayed(summaryview, iter))
2819
                return;
2820

    
2821
        if (summary_is_read_locked(summaryview)) return;
2822
        summary_lock(summaryview);
2823

    
2824
        STATUSBAR_POP(summaryview->mainwin);
2825

    
2826
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter,
2827
                           S_COL_MSG_INFO, &msginfo, -1);
2828

    
2829
        do_mark_read = prefs_common.always_mark_read_on_show_msg;
2830

    
2831
        if (new_window) {
2832
                MessageView *msgview;
2833

    
2834
                msgview = messageview_create_with_new_window();
2835
                val = messageview_show(msgview, msginfo, all_headers);
2836
                do_mark_read = TRUE;
2837
        } else {
2838
                MessageView *msgview = summaryview->messageview;
2839
                gboolean prev_mimeview;
2840

    
2841
                if (!messageview_is_visible(msgview)) {
2842
                        main_window_toggle_message_view(summaryview->mainwin);
2843
                        GTK_EVENTS_FLUSH();
2844
                }
2845
                prev_mimeview =
2846
                        messageview_get_selected_mime_part(msgview) != NULL;
2847

    
2848
                val = messageview_show(msgview, msginfo, all_headers);
2849
                if (prev_mimeview &&
2850
                    !messageview_get_selected_mime_part(msgview))
2851
                        gtk_widget_grab_focus(summaryview->treeview);
2852
        }
2853

    
2854
        if (val == 0 && do_mark_read) {
2855
                if (MSG_IS_NEW(msginfo->flags) ||
2856
                    MSG_IS_UNREAD(msginfo->flags)) {
2857
                        summary_mark_row_as_read(summaryview, iter);
2858
                        if (MSG_IS_IMAP(msginfo->flags))
2859
                                imap_msg_unset_perm_flags
2860
                                        (msginfo, MSG_NEW | MSG_UNREAD);
2861
                        summary_set_row(summaryview, iter, msginfo);
2862
                        summary_status_show(summaryview);
2863
                }
2864
        }
2865

    
2866
        path = gtk_tree_model_get_path
2867
                (GTK_TREE_MODEL(summaryview->store), iter);
2868
        if (!new_window) {
2869
                gtk_tree_row_reference_free(summaryview->displayed);
2870
                summaryview->displayed =
2871
                        gtk_tree_row_reference_new
2872
                                (GTK_TREE_MODEL(summaryview->store), path);
2873
        }
2874
        gtkut_tree_view_scroll_to_cell
2875
                (GTK_TREE_VIEW(summaryview->treeview), path,
2876
                 !summaryview->on_button_press);
2877
        gtk_tree_path_free(path);
2878

    
2879
        if (summaryview->folder_item->sort_key == SORT_BY_UNREAD)
2880
                summary_selection_list_free(summaryview);
2881

    
2882
        summary_set_menu_sensitive(summaryview);
2883
        main_window_set_toolbar_sensitive(summaryview->mainwin);
2884

    
2885
        trayicon_set_tooltip(NULL);
2886
        trayicon_set_notify(FALSE);
2887

    
2888
        statusbar_pop_all();
2889

    
2890
        summary_unlock(summaryview);
2891
}
2892

    
2893
void summary_display_msg_selected(SummaryView *summaryview,
2894
                                  gboolean new_window, gboolean all_headers)
2895
{
2896
        GtkTreeIter iter;
2897

    
2898
        if (summary_is_read_locked(summaryview)) return;
2899

    
2900
        if (summaryview->selected) {
2901
                if (gtkut_tree_row_reference_get_iter
2902
                        (GTK_TREE_MODEL(summaryview->store),
2903
                         summaryview->selected, &iter)) {
2904
                        summary_display_msg_full(summaryview, &iter,
2905
                                                 new_window, all_headers, TRUE);
2906
                }
2907
        }
2908
}
2909

    
2910
void summary_redisplay_msg(SummaryView *summaryview)
2911
{
2912
        GtkTreeIter iter;
2913

    
2914
        if (summaryview->displayed) {
2915
                if (gtkut_tree_row_reference_get_iter
2916
                        (GTK_TREE_MODEL(summaryview->store),
2917
                         summaryview->displayed, &iter)) {
2918
                        summary_display_msg_full(summaryview, &iter,
2919
                                                 FALSE, FALSE, TRUE);
2920
                }
2921
        }
2922
}
2923

    
2924
void summary_open_msg(SummaryView *summaryview)
2925
{
2926
        summary_display_msg_selected(summaryview, TRUE, FALSE);
2927
}
2928

    
2929
static void summary_activate_selected(SummaryView *summaryview)
2930
{
2931
        if (!summaryview->folder_item)
2932
                return;
2933

    
2934
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item))
2935
                summary_reedit(summaryview);
2936
        else
2937
                summary_open_msg(summaryview);
2938

    
2939
        summaryview->display_msg = FALSE;
2940
}
2941

    
2942
void summary_view_source(SummaryView *summaryview)
2943
{
2944
        GtkTreeIter iter;
2945
        MsgInfo *msginfo;
2946
        SourceWindow *srcwin;
2947

    
2948
        if (summaryview->selected) {
2949
                if (gtkut_tree_row_reference_get_iter
2950
                        (GTK_TREE_MODEL(summaryview->store),
2951
                         summaryview->selected, &iter)) {
2952
                        GET_MSG_INFO(msginfo, &iter);
2953

    
2954
                        srcwin = source_window_create();
2955
                        source_window_show_msg(srcwin, msginfo);
2956
                        source_window_show(srcwin);
2957
                }
2958
        }
2959
}
2960

    
2961
void summary_reedit(SummaryView *summaryview)
2962
{
2963
        GtkTreeIter iter;
2964
        MsgInfo *msginfo;
2965

    
2966
        if (!summaryview->selected) return;
2967
        if (!FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) return;
2968

    
2969
        if (gtkut_tree_row_reference_get_iter
2970
                (GTK_TREE_MODEL(summaryview->store),
2971
                 summaryview->selected, &iter)) {
2972
                GET_MSG_INFO(msginfo, &iter);
2973
                compose_reedit(msginfo);
2974
        }
2975
}
2976

    
2977
gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
2978
{
2979
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2980
        GtkTreeIter iter;
2981

    
2982
        if (summary_is_read_locked(summaryview)) return FALSE;
2983

    
2984
        if (!gtkut_tree_row_reference_get_iter
2985
                (model, summaryview->selected, &iter))
2986
                return FALSE;
2987

    
2988
        if (type == GTK_SCROLL_STEP_FORWARD) {
2989
                if (!gtkut_tree_model_next(model, &iter))
2990
                        return FALSE;
2991
        } else {
2992
                if (!gtkut_tree_model_prev(model, &iter))
2993
                        return FALSE;
2994
        }
2995

    
2996
        summary_select_row(summaryview, &iter,
2997
                           messageview_is_visible(summaryview->messageview),
2998
                           FALSE);
2999

    
3000
        return TRUE;
3001
}
3002

    
3003
void summary_toggle_view(SummaryView *summaryview)
3004
{
3005
        if (!messageview_is_visible(summaryview->messageview) &&
3006
            summaryview->selected) {
3007
                summary_display_msg_selected(summaryview, FALSE, FALSE);
3008
                summary_mark_displayed_read(summaryview, NULL);
3009
        } else
3010
                main_window_toggle_message_view(summaryview->mainwin);
3011
}
3012

    
3013
void summary_update_selected_rows(SummaryView *summaryview)
3014
{
3015
        GList *rows, *cur;
3016
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3017
        GtkTreeIter iter;
3018
        GtkTreePath *path;
3019

    
3020
        rows = summary_get_selected_rows(summaryview);
3021
        for (cur = rows; cur != NULL; cur = cur->next) {
3022
                path = (GtkTreePath *)cur->data;
3023
                gtk_tree_model_get_iter(model, &iter, path);
3024
                summary_set_row(summaryview, &iter, NULL);
3025
        }
3026
}
3027

    
3028
void summary_update_by_msgnum(SummaryView *summaryview, guint msgnum)
3029
{
3030
        GtkTreeIter iter;
3031

    
3032
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
3033
                summary_set_row(summaryview, &iter, NULL);
3034
}
3035

    
3036
static void summary_mark_row(SummaryView *summaryview, GtkTreeIter *iter)
3037
{
3038
        MsgInfo *msginfo = NULL;
3039

    
3040
        GET_MSG_INFO(msginfo, iter);
3041

    
3042
        msginfo->to_folder = NULL;
3043
        if (MSG_IS_DELETED(msginfo->flags)) {
3044
                summaryview->deleted--;
3045
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3046
        }
3047
        if (MSG_IS_MOVE(msginfo->flags))
3048
                summaryview->moved--;
3049
        if (MSG_IS_COPY(msginfo->flags))
3050
                summaryview->copied--;
3051
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3052
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
3053
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3054
        summaryview->folder_item->mark_dirty = TRUE;
3055
        summary_set_row(summaryview, iter, msginfo);
3056

    
3057
        debug_print(_("Message %d is marked\n"), msginfo->msgnum);
3058
}
3059

    
3060
void summary_mark(SummaryView *summaryview)
3061
{
3062
        GList *rows, *cur;
3063
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3064
        GtkTreeIter iter;
3065
        FolderSortKey sort_key = SORT_BY_NONE;
3066
        FolderSortType sort_type = SORT_ASCENDING;
3067

    
3068
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3069
            summary_is_read_locked(summaryview))
3070
                return;
3071

    
3072
        summary_lock(summaryview);
3073
        SORT_BLOCK(SORT_BY_MARK);
3074

    
3075
        rows = summary_get_selected_rows(summaryview);
3076
        for (cur = rows; cur != NULL; cur = cur->next) {
3077
                GtkTreePath *path = (GtkTreePath *)cur->data;
3078
                gtk_tree_model_get_iter(model, &iter, path);
3079
                summary_mark_row(summaryview, &iter);
3080
        }
3081

    
3082
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3083
                GSList *msglist;
3084

    
3085
                msglist = summary_get_selected_msg_list(summaryview);
3086
                imap_msg_list_set_perm_flags(msglist, MSG_MARKED);
3087
                g_slist_free(msglist);
3088
        }
3089

    
3090
        SORT_UNBLOCK(SORT_BY_MARK);
3091
        summary_unlock(summaryview);
3092

    
3093
        summary_status_show(summaryview);
3094
}
3095

    
3096
static void summary_mark_row_as_read(SummaryView *summaryview,
3097
                                     GtkTreeIter *iter)
3098
{
3099
        MsgInfo *msginfo = NULL;
3100

    
3101
        GET_MSG_INFO(msginfo, iter);
3102

    
3103
        if (MSG_IS_NEW(msginfo->flags)) {
3104
                if (summaryview->folder_item->new > 0)
3105
                        summaryview->folder_item->new--;
3106
                if (summaryview->on_filter && summaryview->flt_new > 0)
3107
                        summaryview->flt_new--;
3108
                inc_block_notify(TRUE);
3109
        }
3110
        if (MSG_IS_UNREAD(msginfo->flags)) {
3111
                if (summaryview->folder_item->unread > 0)
3112
                        summaryview->folder_item->unread--;
3113
                if (summaryview->on_filter && summaryview->flt_unread > 0)
3114
                        summaryview->flt_unread--;
3115
        }
3116

    
3117
        if (summaryview->folder_item->stype == F_VIRTUAL) {
3118
                if (MSG_IS_NEW(msginfo->flags) && msginfo->folder->new > 0)
3119
                        msginfo->folder->new--;
3120
                if (MSG_IS_UNREAD(msginfo->flags) &&
3121
                    msginfo->folder->unread > 0)
3122
                        msginfo->folder->unread--;
3123
                folderview_update_item(msginfo->folder, FALSE);
3124
        }
3125

    
3126
        if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3127
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
3128
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3129
                summaryview->folder_item->mark_dirty = TRUE;
3130
                summary_set_row(summaryview, iter, msginfo);
3131
                debug_print(_("Message %d is marked as being read\n"),
3132
                            msginfo->msgnum);
3133
        }
3134
}
3135

    
3136
void summary_mark_as_read(SummaryView *summaryview)
3137
{
3138
        GList *rows, *cur;
3139
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3140
        GtkTreeIter iter;
3141
        FolderSortKey sort_key = SORT_BY_NONE;
3142
        FolderSortType sort_type = SORT_ASCENDING;
3143

    
3144
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3145
            summary_is_read_locked(summaryview))
3146
                return;
3147

    
3148
        summary_lock(summaryview);
3149
        SORT_BLOCK(SORT_BY_UNREAD);
3150

    
3151
        rows = summary_get_selected_rows(summaryview);
3152

    
3153
        for (cur = rows; cur != NULL; cur = cur->next) {
3154
                GtkTreePath *path = (GtkTreePath *)cur->data;
3155

    
3156
                gtk_tree_model_get_iter(model, &iter, path);
3157
                summary_mark_row_as_read(summaryview, &iter);
3158
        }
3159

    
3160
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3161
                GSList *msglist;
3162

    
3163
                msglist = summary_get_selected_msg_list(summaryview);
3164
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3165
                g_slist_free(msglist);
3166
        }
3167

    
3168
        SORT_UNBLOCK(SORT_BY_UNREAD);
3169
        summary_unlock(summaryview);
3170

    
3171
        trayicon_set_tooltip(NULL);
3172
        trayicon_set_notify(FALSE);
3173
        summary_status_show(summaryview);
3174
}
3175

    
3176
static gboolean prepend_thread_rows_func(GtkTreeModel *model, GtkTreePath *path,
3177
                                         GtkTreeIter *iter, gpointer data)
3178
{
3179
        GSList **thr_rows = (GSList **)data;
3180
        GtkTreePath *top_path;
3181

    
3182
        top_path = gtk_tree_path_copy(path);
3183
        *thr_rows = g_slist_prepend(*thr_rows, top_path);
3184
        return FALSE;
3185
}
3186

    
3187
void summary_mark_thread_as_read(SummaryView *summaryview)
3188
{
3189
        GList *rows, *cur;
3190
        GSList *thr_rows = NULL, *top_rows = NULL, *s_cur;
3191
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3192
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3193
        GtkTreeIter iter, parent;
3194
        GtkTreePath *path, *top_path;
3195
        GHashTable *row_table;
3196
        MsgInfo *msginfo;
3197
        GSList *msglist = NULL;
3198
        FolderSortKey sort_key = SORT_BY_NONE;
3199
        FolderSortType sort_type = SORT_ASCENDING;
3200

    
3201
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3202
            summary_is_read_locked(summaryview))
3203
                return;
3204

    
3205
        summary_lock(summaryview);
3206
        SORT_BLOCK(SORT_BY_UNREAD);
3207

    
3208
        rows = summary_get_selected_rows(summaryview);
3209

    
3210
        row_table = g_hash_table_new(NULL, NULL);
3211

    
3212
        for (cur = rows; cur != NULL; cur = cur->next) {
3213
                path = (GtkTreePath *)cur->data;
3214
                gtk_tree_model_get_iter(model, &iter, path);
3215
                while (gtk_tree_model_iter_parent(model, &parent, &iter))
3216
                        iter = parent;
3217
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3218
                if (!g_hash_table_lookup(row_table, msginfo)) {
3219
                        g_hash_table_insert(row_table, msginfo,
3220
                                            GINT_TO_POINTER(1));
3221
                        top_path = gtk_tree_model_get_path(model, &iter);
3222
                        top_rows = g_slist_prepend(top_rows, top_path);
3223
                        gtkut_tree_model_foreach(model, &iter,
3224
                                                 prepend_thread_rows_func,
3225
                                                 &thr_rows);
3226
                }
3227
        }
3228
        top_rows = g_slist_reverse(top_rows);
3229
        thr_rows = g_slist_reverse(thr_rows);
3230

    
3231
        g_hash_table_destroy(row_table);
3232

    
3233
        for (s_cur = thr_rows; s_cur != NULL; s_cur = s_cur->next) {
3234
                path = (GtkTreePath *)s_cur->data;
3235
                gtk_tree_model_get_iter(model, &iter, path);
3236
                summary_mark_row_as_read(summaryview, &iter);
3237
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3238
                msglist = g_slist_prepend(msglist, msginfo);
3239
        }
3240

    
3241
        if (prefs_common.bold_unread) {
3242
                for (s_cur = top_rows; s_cur != NULL; s_cur = s_cur->next) {
3243
                        path = (GtkTreePath *)s_cur->data;
3244
                        gtk_tree_model_get_iter(model, &iter, path);
3245
                        if (gtk_tree_model_iter_has_child(model, &iter) &&
3246
                            !gtk_tree_view_row_expanded(treeview, path) &&
3247
                            !summary_have_unread_children(summaryview, &iter)) {
3248
                                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
3249
                                                   S_COL_BOLD,
3250
                                                   PANGO_WEIGHT_NORMAL, -1);
3251
                        }
3252
                }
3253
        }
3254
        msglist = g_slist_reverse(msglist);
3255

    
3256
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3257
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3258
        }
3259

    
3260
        g_slist_free(msglist);
3261
        g_slist_foreach(top_rows, (GFunc)gtk_tree_path_free, NULL);
3262
        g_slist_free(top_rows);
3263
        g_slist_foreach(thr_rows, (GFunc)gtk_tree_path_free, NULL);
3264
        g_slist_free(thr_rows);
3265

    
3266
        SORT_UNBLOCK(SORT_BY_UNREAD);
3267
        summary_unlock(summaryview);
3268

    
3269
        trayicon_set_tooltip(NULL);
3270
        trayicon_set_notify(FALSE);
3271
        summary_status_show(summaryview);
3272
}
3273

    
3274
void summary_mark_all_read(SummaryView *summaryview)
3275
{
3276
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3277
        GtkTreeIter iter;
3278
        gboolean valid;
3279
        FolderSortKey sort_key = SORT_BY_NONE;
3280
        FolderSortType sort_type = SORT_ASCENDING;
3281

    
3282
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3283
            summary_is_read_locked(summaryview))
3284
                return;
3285

    
3286
        summary_lock(summaryview);
3287
        SORT_BLOCK(SORT_BY_UNREAD);
3288

    
3289
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3290
                GSList *msglist;
3291
                msglist = summary_get_flagged_msg_list(summaryview,
3292
                                                       MSG_NEW | MSG_UNREAD);
3293
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3294
                g_slist_free(msglist);
3295
        }
3296

    
3297
        valid = gtk_tree_model_get_iter_first(model, &iter);
3298
        while (valid) {
3299
                summary_mark_row_as_read(summaryview, &iter);
3300
                if (prefs_common.bold_unread) {
3301
                        if (gtk_tree_model_iter_has_child(model, &iter)) {
3302
                                GtkTreePath *path;
3303

    
3304
                                path = gtk_tree_model_get_path(model, &iter);
3305
                                if (!gtk_tree_view_row_expanded
3306
                                        (GTK_TREE_VIEW(summaryview->treeview),
3307
                                         path))
3308
                                        gtk_tree_store_set
3309
                                                (GTK_TREE_STORE(model), &iter,
3310
                                                 S_COL_BOLD,
3311
                                                 PANGO_WEIGHT_NORMAL, -1);
3312
                                gtk_tree_path_free(path);
3313
                        }
3314
                }
3315
                valid = gtkut_tree_model_next(model, &iter);
3316
        }
3317

    
3318
        SORT_UNBLOCK(SORT_BY_UNREAD);
3319
        summary_unlock(summaryview);
3320

    
3321
        trayicon_set_tooltip(NULL);
3322
        trayicon_set_notify(FALSE);
3323
        summary_status_show(summaryview);
3324
}
3325

    
3326
static void summary_mark_row_as_unread(SummaryView *summaryview,
3327
                                       GtkTreeIter *iter)
3328
{
3329
        MsgInfo *msginfo = NULL;
3330

    
3331
        GET_MSG_INFO(msginfo, iter);
3332

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

    
3357
void summary_mark_as_unread(SummaryView *summaryview)
3358
{
3359
        GList *rows, *cur;
3360
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3361
        GtkTreeIter iter;
3362
        FolderSortKey sort_key = SORT_BY_NONE;
3363
        FolderSortType sort_type = SORT_ASCENDING;
3364

    
3365
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3366
            summary_is_read_locked(summaryview))
3367
                return;
3368

    
3369
        summary_lock(summaryview);
3370
        SORT_BLOCK(SORT_BY_UNREAD);
3371

    
3372
        rows = summary_get_selected_rows(summaryview);
3373
        for (cur = rows; cur != NULL; cur = cur->next) {
3374
                GtkTreePath *path = (GtkTreePath *)cur->data;
3375

    
3376
                gtk_tree_model_get_iter(model, &iter, path);
3377
                summary_mark_row_as_unread(summaryview, &iter);
3378
        }
3379

    
3380
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3381
                GSList *msglist;
3382
                msglist = summary_get_selected_msg_list(summaryview);
3383
                imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED);
3384
                imap_msg_list_set_perm_flags(msglist, MSG_UNREAD);
3385
                g_slist_free(msglist);
3386
        }
3387

    
3388
        SORT_UNBLOCK(SORT_BY_UNREAD);
3389
        summary_unlock(summaryview);
3390

    
3391
        summary_status_show(summaryview);
3392
}
3393

    
3394
static void summary_delete_row(SummaryView *summaryview, GtkTreeIter *iter)
3395
{
3396
        MsgInfo *msginfo = NULL;
3397

    
3398
        GET_MSG_INFO(msginfo, iter);
3399

    
3400
        if (MSG_IS_DELETED(msginfo->flags)) return;
3401

    
3402
        msginfo->to_folder = NULL;
3403
        if (MSG_IS_MOVE(msginfo->flags)) {
3404
                summaryview->moved--;
3405
                if (summaryview->on_filter)
3406
                        summaryview->flt_moved--;
3407
        }
3408
        if (MSG_IS_COPY(msginfo->flags)) {
3409
                summaryview->copied--;
3410
                if (summaryview->on_filter)
3411
                        summaryview->flt_copied--;
3412
        }
3413
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3414
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3415
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3416
        summaryview->deleted++;
3417
        if (summaryview->on_filter)
3418
                summaryview->flt_deleted++;
3419
        summaryview->folder_item->mark_dirty = TRUE;
3420

    
3421
        if (!prefs_common.immediate_exec && summaryview->tmp_flag == 0)
3422
                summary_set_row(summaryview, iter, msginfo);
3423

    
3424
        debug_print(_("Message %s/%d is set to delete\n"),
3425
                    msginfo->folder->path, msginfo->msgnum);
3426
}
3427

    
3428
static gboolean summary_delete_foreach_func(GtkTreeModel *model,
3429
                                            GtkTreePath *path,
3430
                                            GtkTreeIter *iter, gpointer data)
3431
{
3432
        summary_delete_row((SummaryView *)data, iter);
3433
        return FALSE;
3434
}
3435

    
3436
void summary_delete(SummaryView *summaryview)
3437
{
3438
        FolderItem *item = summaryview->folder_item;
3439
        GList *rows, *cur;
3440
        GtkTreeIter last_sel, next;
3441
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3442
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3443
        gboolean is_trash;
3444

    
3445
        if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return;
3446

    
3447
        if (summary_is_locked(summaryview)) return;
3448

    
3449
        /* if current folder is trash, ask for confirmation */
3450
        is_trash = folder_item_is_trash(item);
3451
        if (is_trash) {
3452
                AlertValue aval;
3453

    
3454
                aval = alertpanel(_("Delete message(s)"),
3455
                                  _("Do you really want to delete message(s) from the trash?"),
3456
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3457
                if (aval != G_ALERTDEFAULT) return;
3458
        }
3459

    
3460
        rows = summary_get_selected_rows(summaryview);
3461
        if (!rows)
3462
                return;
3463

    
3464
        summaryview->tmp_flag = is_trash ? 1 : 0;
3465

    
3466
        /* next code sets current row focus right. We need to find a row
3467
         * that is not deleted. */
3468
        for (cur = rows; cur != NULL; cur = cur->next) {
3469
                GtkTreePath *path = (GtkTreePath *)cur->data;
3470

    
3471
                gtk_tree_model_get_iter(model, &last_sel, path);
3472
                if (gtk_tree_model_iter_has_child(model, &last_sel) &&
3473
                    !gtk_tree_view_row_expanded(treeview, path)) {
3474
                        gtkut_tree_model_foreach
3475
                                (model, &last_sel, summary_delete_foreach_func,
3476
                                 summaryview);
3477
                } else
3478
                        summary_delete_row(summaryview, &last_sel);
3479
        }
3480

    
3481
        summaryview->tmp_flag = 0;
3482

    
3483
        if (prefs_common.immediate_exec || is_trash) {
3484
                summary_execute(summaryview);
3485
        } else {
3486
                if (summary_find_nearest_msg(summaryview, &next, &last_sel)) {
3487
                        summary_select_row
3488
                                (summaryview, &next,
3489
                                 messageview_is_visible
3490
                                        (summaryview->messageview),
3491
                                 FALSE);
3492
                }
3493
                summary_status_show(summaryview);
3494
        }
3495
}
3496

    
3497
static gboolean summary_delete_duplicated_func(GtkTreeModel *model,
3498
                                               GtkTreePath *path,
3499
                                               GtkTreeIter *iter,
3500
                                               gpointer data)
3501
{
3502
        SummaryView *summaryview = (SummaryView *)data;
3503
        MsgInfo *msginfo;
3504
        GtkTreeIter *found;
3505
        GtkTreePath *found_path;
3506

    
3507
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3508

    
3509
        if (!msginfo || !msginfo->msgid || !*msginfo->msgid)
3510
                return FALSE;
3511

    
3512
        found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
3513

    
3514
        if (found) {
3515
                found_path = gtk_tree_model_get_path(model, found);
3516
                if (gtk_tree_path_compare(path, found_path) != 0)
3517
                        summary_delete_row(summaryview, iter);
3518
                gtk_tree_path_free(found_path);
3519
        }
3520

    
3521
        return FALSE;
3522
}
3523

    
3524
void summary_delete_duplicated(SummaryView *summaryview)
3525
{
3526
        if (!summaryview->folder_item ||
3527
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3528
        if (folder_item_is_trash(summaryview->folder_item)) return;
3529

    
3530
        if (summary_is_locked(summaryview)) return;
3531

    
3532
        main_window_cursor_wait(summaryview->mainwin);
3533
        debug_print("Deleting duplicated messages...");
3534
        STATUSBAR_PUSH(summaryview->mainwin,
3535
                       _("Deleting duplicated messages..."));
3536

    
3537
        summary_msgid_table_create(summaryview);
3538

    
3539
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
3540
                               summary_delete_duplicated_func, summaryview);
3541

    
3542
        summary_msgid_table_destroy(summaryview);
3543

    
3544
        if (prefs_common.immediate_exec)
3545
                summary_execute(summaryview);
3546
        else
3547
                summary_status_show(summaryview);
3548

    
3549
        debug_print("done.\n");
3550
        STATUSBAR_POP(summaryview->mainwin);
3551
        main_window_cursor_normal(summaryview->mainwin);
3552
}
3553

    
3554
static void summary_unmark_row(SummaryView *summaryview, GtkTreeIter *iter)
3555
{
3556
        MsgInfo *msginfo = NULL;
3557

    
3558
        GET_MSG_INFO(msginfo, iter);
3559

    
3560
        msginfo->to_folder = NULL;
3561
        if (MSG_IS_DELETED(msginfo->flags)) {
3562
                summaryview->deleted--;
3563
                if (summaryview->on_filter)
3564
                        summaryview->flt_deleted--;
3565
        }
3566
        if (MSG_IS_MOVE(msginfo->flags)) {
3567
                summaryview->moved--;
3568
                if (summaryview->on_filter)
3569
                        summaryview->flt_moved--;
3570
        }
3571
        if (MSG_IS_COPY(msginfo->flags)) {
3572
                summaryview->copied--;
3573
                if (summaryview->on_filter)
3574
                        summaryview->flt_copied--;
3575
        }
3576
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3577
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3578
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3579
        summaryview->folder_item->mark_dirty = TRUE;
3580
        summary_set_row(summaryview, iter, msginfo);
3581

    
3582
        debug_print(_("Message %s/%d is unmarked\n"),
3583
                    msginfo->folder->path, msginfo->msgnum);
3584
}
3585

    
3586
void summary_unmark(SummaryView *summaryview)
3587
{
3588
        GList *rows, *cur;
3589
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3590
        GtkTreeIter iter;
3591
        FolderSortKey sort_key = SORT_BY_NONE;
3592
        FolderSortType sort_type = SORT_ASCENDING;
3593

    
3594
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP &&
3595
            summary_is_read_locked(summaryview))
3596
                return;
3597

    
3598
        summary_lock(summaryview);
3599
        SORT_BLOCK(SORT_BY_MARK);
3600

    
3601
        rows = summary_get_selected_rows(summaryview);
3602
        for (cur = rows; cur != NULL; cur = cur->next) {
3603
                GtkTreePath *path = (GtkTreePath *)cur->data;
3604
                gtk_tree_model_get_iter(model, &iter, path);
3605
                summary_unmark_row(summaryview, &iter);
3606
        }
3607

    
3608
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3609
                GSList *msglist;
3610

    
3611
                msglist = summary_get_selected_msg_list(summaryview);
3612
                imap_msg_list_unset_perm_flags(msglist, MSG_MARKED);
3613
                g_slist_free(msglist);
3614
        }
3615

    
3616
        SORT_UNBLOCK(SORT_BY_MARK);
3617
        summary_unlock(summaryview);
3618

    
3619
        summary_status_show(summaryview);
3620
}
3621

    
3622
static void summary_move_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3623
                                FolderItem *to_folder)
3624
{
3625
        MsgInfo *msginfo;
3626

    
3627
        g_return_if_fail(to_folder != NULL);
3628
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3629
                return;
3630

    
3631
        GET_MSG_INFO(msginfo, iter);
3632

    
3633
        msginfo->to_folder = to_folder;
3634
        if (MSG_IS_DELETED(msginfo->flags)) {
3635
                summaryview->deleted--;
3636
                if (summaryview->on_filter)
3637
                        summaryview->flt_deleted--;
3638
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3639
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3640
        }
3641
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3642
        if (!MSG_IS_MOVE(msginfo->flags)) {
3643
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3644
                summaryview->moved++;
3645
                if (summaryview->on_filter)
3646
                        summaryview->flt_moved++;
3647
        }
3648
        summaryview->folder_item->mark_dirty = TRUE;
3649
        if (!prefs_common.immediate_exec)
3650
                summary_set_row(summaryview, iter, msginfo);
3651

    
3652
        debug_print(_("Message %d is set to move to %s\n"),
3653
                    msginfo->msgnum, to_folder->path);
3654
}
3655

    
3656
static gboolean summary_move_foreach_func(GtkTreeModel *model,
3657
                                          GtkTreePath *path, GtkTreeIter *iter,
3658
                                          gpointer data)
3659
{
3660
        SummaryView *summaryview = (SummaryView *)data;
3661

    
3662
        summary_move_row_to(summaryview, iter, summaryview->to_folder);
3663
        return FALSE;
3664
}
3665

    
3666
void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3667
{
3668
        GList *rows, *cur;
3669
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3670
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3671
        GtkTreeIter iter;
3672

    
3673
        if (!to_folder) return;
3674
        if (!summaryview->folder_item ||
3675
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
3676
                return;
3677
        if (summaryview->folder_item->stype == F_QUEUE ||
3678
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3679
                return;
3680

    
3681
        if (summary_is_locked(summaryview)) return;
3682

    
3683
        if (summaryview->folder_item == to_folder) {
3684
                alertpanel_warning(_("Destination is same as current folder."));
3685
                return;
3686
        }
3687

    
3688
        rows = summary_get_selected_rows(summaryview);
3689
        for (cur = rows; cur != NULL; cur = cur->next) {
3690
                GtkTreePath *path = (GtkTreePath *)cur->data;
3691

    
3692
                gtk_tree_model_get_iter(model, &iter, path);
3693
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3694
                    !gtk_tree_view_row_expanded(treeview, path)) {
3695
                        summaryview->to_folder = to_folder;
3696
                        gtkut_tree_model_foreach
3697
                                (model, &iter, summary_move_foreach_func,
3698
                                 summaryview);
3699
                        summaryview->to_folder = NULL;
3700
                } else
3701
                        summary_move_row_to(summaryview, &iter, to_folder);
3702
        }
3703

    
3704
        if (prefs_common.immediate_exec)
3705
                summary_execute(summaryview);
3706
        else {
3707
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3708
                summary_status_show(summaryview);
3709
        }
3710
}
3711

    
3712
void summary_move_to(SummaryView *summaryview)
3713
{
3714
        FolderItem *to_folder;
3715

    
3716
        if (!summaryview->folder_item ||
3717
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3718

    
3719
        to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3720
                                              FOLDER_SEL_MOVE, NULL,
3721
                                              _("Select folder to move"));
3722
        summary_move_selected_to(summaryview, to_folder);
3723
}
3724

    
3725
static void summary_copy_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3726
                                FolderItem *to_folder)
3727
{
3728
        MsgInfo *msginfo;
3729

    
3730
        g_return_if_fail(to_folder != NULL);
3731
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3732
                return;
3733

    
3734
        GET_MSG_INFO(msginfo, iter);
3735

    
3736
        msginfo->to_folder = to_folder;
3737
        if (MSG_IS_DELETED(msginfo->flags)) {
3738
                summaryview->deleted--;
3739
                if (summaryview->on_filter)
3740
                        summaryview->flt_deleted--;
3741
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3742
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3743
        }
3744
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3745
        if (!MSG_IS_COPY(msginfo->flags)) {
3746
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3747
                summaryview->copied++;
3748
                if (summaryview->on_filter)
3749
                        summaryview->flt_copied++;
3750
        }
3751
        summaryview->folder_item->mark_dirty = TRUE;
3752
        if (!prefs_common.immediate_exec)
3753
                summary_set_row(summaryview, iter, msginfo);
3754

    
3755
        debug_print(_("Message %d is set to copy to %s\n"),
3756
                    msginfo->msgnum, to_folder->path);
3757
}
3758

    
3759
static gboolean summary_copy_foreach_func(GtkTreeModel *model,
3760
                                          GtkTreePath *path, GtkTreeIter *iter,
3761
                                          gpointer data)
3762
{
3763
        SummaryView *summaryview = (SummaryView *)data;
3764

    
3765
        summary_copy_row_to(summaryview, iter, summaryview->to_folder);
3766
        return FALSE;
3767
}
3768

    
3769
void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3770
{
3771
        GList *rows, *cur;
3772
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3773
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3774
        GtkTreeIter iter;
3775

    
3776
        if (!to_folder) return;
3777
        if (!summaryview->folder_item) return;
3778
        if (summaryview->folder_item->stype == F_QUEUE ||
3779
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3780
                return;
3781

    
3782
        if (summary_is_locked(summaryview)) return;
3783

    
3784
        if (summaryview->folder_item == to_folder) {
3785
                alertpanel_warning
3786
                        (_("Destination for copy is same as current folder."));
3787
                return;
3788
        }
3789

    
3790
        rows = summary_get_selected_rows(summaryview);
3791
        for (cur = rows; cur != NULL; cur = cur->next) {
3792
                GtkTreePath *path = (GtkTreePath *)cur->data;
3793

    
3794
                gtk_tree_model_get_iter(model, &iter, path);
3795
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3796
                    !gtk_tree_view_row_expanded(treeview, path)) {
3797
                        summaryview->to_folder = to_folder;
3798
                        gtkut_tree_model_foreach
3799
                                 (model, &iter, summary_copy_foreach_func,
3800
                                  summaryview);
3801
                        summaryview->to_folder = NULL;
3802
                } else
3803
                        summary_copy_row_to(summaryview, &iter, to_folder);
3804
        }
3805

    
3806
        if (prefs_common.immediate_exec)
3807
                summary_execute(summaryview);
3808
        else {
3809
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3810
                summary_status_show(summaryview);
3811
        }
3812
}
3813

    
3814
void summary_copy_to(SummaryView *summaryview)
3815
{
3816
        FolderItem *to_folder;
3817

    
3818
        if (!summaryview->folder_item) return;
3819

    
3820
        to_folder = foldersel_folder_sel_full(summaryview->folder_item->folder,
3821
                                              FOLDER_SEL_COPY, NULL,
3822
                                              _("Select folder to copy"));
3823
        summary_copy_selected_to(summaryview, to_folder);
3824
}
3825

    
3826
void summary_add_address(SummaryView *summaryview)
3827
{
3828
        GtkTreeIter iter;
3829
        MsgInfo *msginfo = NULL;
3830
        gchar from[BUFFSIZE];
3831

    
3832
        if (!summaryview->selected) return;
3833

    
3834
        if (!gtkut_tree_row_reference_get_iter
3835
                (GTK_TREE_MODEL(summaryview->store),
3836
                 summaryview->selected, &iter))
3837
                return;
3838

    
3839
        GET_MSG_INFO(msginfo, &iter);
3840
        strncpy2(from, msginfo->from, sizeof(from));
3841
        eliminate_address_comment(from);
3842
        extract_address(from);
3843
        addressbook_add_contact(msginfo->fromname, from, NULL);
3844
}
3845

    
3846
void summary_select_all(SummaryView *summaryview)
3847
{
3848
        gtk_tree_selection_select_all(summaryview->selection);
3849
}
3850

    
3851
void summary_unselect_all(SummaryView *summaryview)
3852
{
3853
        gtk_tree_selection_unselect_all(summaryview->selection);
3854
}
3855

    
3856
void summary_select_thread(SummaryView *summaryview)
3857
{
3858
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3859
        GtkTreeIter iter, parent, child, next;
3860
        GtkTreePath *start_path, *end_path;
3861
        gboolean valid;
3862

    
3863
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
3864
                                                  &iter);
3865
        if (!valid)
3866
                return;
3867

    
3868
        while (gtk_tree_model_iter_parent(model, &parent, &iter))
3869
                iter = parent;
3870

    
3871
        if (!gtk_tree_model_iter_children(model, &child, &iter))
3872
                return;
3873

    
3874
        start_path = gtk_tree_model_get_path(model, &iter);
3875

    
3876
        for (;;) {
3877
                next = iter = child;
3878
                while (gtk_tree_model_iter_next(model, &next))
3879
                        iter = next;
3880
                if (!gtk_tree_model_iter_children(model, &child, &iter))
3881
                        break;
3882
        }
3883

    
3884
        end_path = gtk_tree_model_get_path(model, &iter);
3885

    
3886
        gtk_tree_view_expand_row(GTK_TREE_VIEW(summaryview->treeview),
3887
                                 start_path, TRUE);
3888
        gtk_tree_selection_select_range(summaryview->selection,
3889
                                        start_path, end_path);
3890

    
3891
        gtk_tree_path_free(end_path);
3892
        gtk_tree_path_free(start_path);
3893
}
3894

    
3895
void summary_save_as(SummaryView *summaryview)
3896
{
3897
        GtkTreeIter iter;
3898
        MsgInfo *msginfo = NULL;
3899
        gchar *filename;
3900
        gchar *src, *dest;
3901
        FileselFileType types[4] = {{NULL, NULL}};
3902
        gint selected_type = 0;
3903
        gint result;
3904
        gboolean all_headers;
3905

    
3906
        if (!summaryview->selected) return;
3907
        if (!gtkut_tree_row_reference_get_iter
3908
                (GTK_TREE_MODEL(summaryview->store),
3909
                 summaryview->selected, &iter))
3910
                return;
3911

    
3912
        GET_MSG_INFO(msginfo, &iter);
3913
        if (!msginfo) return;
3914

    
3915
        if (msginfo->subject && *msginfo->subject) {
3916
                filename = g_strdup_printf("%s.eml", msginfo->subject);
3917
        } else {
3918
                filename = g_strdup_printf("%u.eml", msginfo->msgnum);
3919
        }
3920
        subst_for_filename(filename);
3921

    
3922
        types[0].type = _("Original (EML/RFC 822)");
3923
        types[0].ext = "eml";
3924
        types[1].type = _("Text");
3925
        types[1].ext = "txt";
3926
        types[2].type = _("Text (UTF-8)");
3927
        types[2].ext = "txt";
3928
        
3929
        dest = filesel_save_as_type(filename, types, prefs_common.save_file_type, &selected_type);
3930

    
3931
        g_free(filename);
3932
        if (!dest)
3933
                return;
3934

    
3935
        debug_print("summary_save_as: selected_type: %d\n", selected_type);
3936

    
3937
        all_headers = summaryview->messageview->textview->show_all_headers;
3938

    
3939
        if (selected_type == 1) {
3940
                result = procmsg_save_message_as_text(msginfo, dest, conv_get_locale_charset_str(), all_headers);
3941
        } else if (selected_type == 2) {
3942
                result = procmsg_save_message_as_text(msginfo, dest, NULL, all_headers);
3943
        } else {
3944
                src = procmsg_get_message_file(msginfo);
3945
                result = copy_file(src, dest, TRUE);
3946
                g_free(src);
3947
        }
3948

    
3949
        if (result < 0) {
3950
                gchar *utf8_dest;
3951

    
3952
                utf8_dest = conv_filename_to_utf8(dest);
3953
                alertpanel_error(_("Can't save the file `%s'."),
3954
                                 g_basename(utf8_dest));
3955
                g_free(utf8_dest);
3956
        }
3957

    
3958
        g_free(dest);
3959

    
3960
        prefs_common.save_file_type = selected_type;
3961
}
3962

    
3963
void summary_print(SummaryView *summaryview)
3964
{
3965
        GSList *mlist;
3966
        gboolean all_headers;
3967

    
3968
        all_headers = summaryview->messageview->textview->show_all_headers;
3969
        mlist = summary_get_selected_msg_list(summaryview);
3970
        if (!mlist)
3971
                return;
3972
        printing_print_messages(mlist, all_headers);
3973
        g_slist_free(mlist);
3974
}
3975

    
3976
gboolean summary_execute(SummaryView *summaryview)
3977
{
3978
        gint val = 0;
3979

    
3980
        if (!summaryview->folder_item) return FALSE;
3981

    
3982
        if (summary_is_locked(summaryview)) return FALSE;
3983
        summary_lock(summaryview);
3984

    
3985
        val |= summary_execute_move(summaryview);
3986
        val |= summary_execute_copy(summaryview);
3987
        val |= summary_execute_delete(summaryview);
3988

    
3989
        summary_unlock(summaryview);
3990

    
3991
        summary_remove_invalid_messages(summaryview);
3992

    
3993
        statusbar_pop_all();
3994
        STATUSBAR_POP(summaryview->mainwin);
3995

    
3996
        if (val != 0) {
3997
                alertpanel_error(_("Error occurred while processing messages."));
3998
        }
3999

    
4000
        return TRUE;
4001
}
4002

    
4003
static void summary_remove_invalid_messages(SummaryView *summaryview)
4004
{
4005
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4006
        MsgInfo *disp_msginfo = NULL, *msginfo;
4007
        FolderItem *item = summaryview->folder_item;
4008
        GtkTreeIter iter, next;
4009
        GtkTreePath *path;
4010
        gboolean valid;
4011

    
4012
        /* get currently displayed message */
4013
        if (summaryview->displayed) {
4014
                GtkTreeIter displayed;
4015

    
4016
                valid = gtkut_tree_row_reference_get_iter
4017
                        (model, summaryview->displayed, &displayed);
4018
                if (valid) {
4019
                        gtk_tree_model_get(model, &displayed,
4020
                                           S_COL_MSG_INFO, &disp_msginfo, -1);
4021
                        if (MSG_IS_INVALID(disp_msginfo->flags)) {
4022
                                valid = FALSE;
4023
                                disp_msginfo = NULL;
4024
                        }
4025
                }
4026
                if (!valid) {
4027
                        /* g_print("displayed became invalid before removing\n"); */
4028
                        messageview_clear(summaryview->messageview);
4029
                        gtk_tree_row_reference_free(summaryview->displayed);
4030
                        summaryview->displayed = NULL;
4031
                }
4032
        }
4033

    
4034
        if (item->threaded)
4035
                summary_modify_threads(summaryview);
4036

    
4037
        /* update selection */
4038
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
4039
                                                  &iter);
4040
        if (valid) {
4041
                valid = summary_find_nearest_msg(summaryview, &next, &iter);
4042
                if (valid) {
4043
                        gboolean display;
4044

    
4045
                        gtk_tree_model_get(model, &next,
4046
                                           S_COL_MSG_INFO, &msginfo, -1);
4047
                        if (disp_msginfo && disp_msginfo == msginfo) {
4048
                                /* g_print("replace displayed\n"); */
4049
                                path = gtk_tree_model_get_path(model, &next);
4050
                                gtk_tree_row_reference_free
4051
                                        (summaryview->displayed);
4052
                                summaryview->displayed =
4053
                                        gtk_tree_row_reference_new(model, path);
4054
                                gtk_tree_path_free(path);
4055
                        }
4056
                        display = prefs_common.immediate_exec &&
4057
                                messageview_is_visible
4058
                                        (summaryview->messageview);
4059
                        summary_select_row
4060
                                (summaryview, &next, display, FALSE);
4061
                        if (display)
4062
                                summary_mark_displayed_read(summaryview, &next);
4063
                }
4064
        }
4065
        if (!valid)
4066
                summary_unselect_all(summaryview);
4067

    
4068
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
4069
             valid == TRUE; iter = next) {
4070
                next = iter;
4071
                valid = gtkut_tree_model_next(model, &next);
4072

    
4073
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4074
                if (!MSG_IS_INVALID(msginfo->flags))
4075
                        continue;
4076

    
4077
                if (gtk_tree_model_iter_has_child(model, &iter)) {
4078
                        g_warning("summary_remove_invalid_messages(): "
4079
                                  "tried to remove row which has child\n");
4080
                        continue;
4081
                }
4082

    
4083
                gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
4084
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4085
                                                        msginfo);
4086
                if (summaryview->flt_mlist)
4087
                        summaryview->flt_mlist =
4088
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4089
                procmsg_msginfo_free(msginfo);
4090

    
4091
                item->cache_dirty = TRUE;
4092
        }
4093

    
4094
        /* selection list becomes invalid if modified */
4095
        if (item->cache_dirty)
4096
                summary_selection_list_free(summaryview);
4097

    
4098
        if (summaryview->displayed &&
4099
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4100
                /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */
4101
                if (disp_msginfo &&
4102
                    gtkut_tree_model_find_by_column_data
4103
                        (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) {
4104
                        /* g_print("replace displayed\n"); */
4105
                        path = gtk_tree_model_get_path(model, &iter);
4106
                        gtk_tree_row_reference_free(summaryview->displayed);
4107
                        summaryview->displayed =
4108
                                gtk_tree_row_reference_new(model, path);
4109
                        gtk_tree_path_free(path);
4110
                } else {
4111
                        messageview_clear(summaryview->messageview);
4112
                        gtk_tree_row_reference_free(summaryview->displayed);
4113
                        summaryview->displayed = NULL;
4114
                }
4115
        }
4116

    
4117
        if (gtk_tree_model_get_iter_first(model, &iter))
4118
                gtk_widget_grab_focus(summaryview->treeview);
4119
        else {
4120
                menu_set_insensitive_all
4121
                        (GTK_MENU_SHELL(summaryview->popupmenu));
4122
                gtk_widget_grab_focus(summaryview->folderview->treeview);
4123
        }
4124

    
4125
        summary_write_cache(summaryview);
4126

    
4127
        summary_update_status(summaryview);
4128
        summary_status_show(summaryview);
4129
}
4130

    
4131
static gboolean summary_execute_move_func(GtkTreeModel *model,
4132
                                          GtkTreePath *path, GtkTreeIter *iter,
4133
                                          gpointer data)
4134
{
4135
        SummaryView *summaryview = data;
4136
        MsgInfo *msginfo;
4137

    
4138
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4139

    
4140
        if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
4141
                g_hash_table_insert(summaryview->folder_table,
4142
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4143

    
4144
                summaryview->tmp_mlist =
4145
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4146

    
4147
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
4148
                summary_set_row(summaryview, iter, msginfo);
4149
        }
4150

    
4151
        return FALSE;
4152
}
4153

    
4154
static gint summary_execute_move(SummaryView *summaryview)
4155
{
4156
        gint val = 0;
4157

    
4158
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4159

    
4160
        /* search moving messages and execute */
4161
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4162
                               summary_execute_move_func, summaryview);
4163

    
4164
        if (summaryview->tmp_mlist) {
4165
                summaryview->tmp_mlist =
4166
                        g_slist_reverse(summaryview->tmp_mlist);
4167
                val = procmsg_move_messages(summaryview->tmp_mlist);
4168

    
4169
                folderview_update_item_foreach(summaryview->folder_table,
4170
                                               FALSE);
4171

    
4172
                g_slist_free(summaryview->tmp_mlist);
4173
                summaryview->tmp_mlist = NULL;
4174
        }
4175

    
4176
        g_hash_table_destroy(summaryview->folder_table);
4177
        summaryview->folder_table = NULL;
4178

    
4179
        return val;
4180
}
4181

    
4182
static gboolean summary_execute_copy_func(GtkTreeModel *model,
4183
                                          GtkTreePath *path, GtkTreeIter *iter,
4184
                                          gpointer data)
4185
{
4186
        SummaryView *summaryview = data;
4187
        MsgInfo *msginfo;
4188

    
4189
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4190

    
4191
        if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
4192
                g_hash_table_insert(summaryview->folder_table,
4193
                                    msginfo->to_folder, GINT_TO_POINTER(1));
4194

    
4195
                summaryview->tmp_mlist =
4196
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4197

    
4198
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
4199
                summary_set_row(summaryview, iter, msginfo);
4200
        }
4201

    
4202
        return FALSE;
4203
}
4204

    
4205
static gint summary_execute_copy(SummaryView *summaryview)
4206
{
4207
        gint val = 0;
4208

    
4209
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
4210

    
4211
        /* search copying messages and execute */
4212
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4213
                               summary_execute_copy_func, summaryview);
4214

    
4215
        if (summaryview->tmp_mlist) {
4216
                summaryview->tmp_mlist =
4217
                        g_slist_reverse(summaryview->tmp_mlist);
4218
                val = procmsg_copy_messages(summaryview->tmp_mlist);
4219

    
4220
                folderview_update_item_foreach(summaryview->folder_table,
4221
                                               FALSE);
4222

    
4223
                g_slist_free(summaryview->tmp_mlist);
4224
                summaryview->tmp_mlist = NULL;
4225
        }
4226

    
4227
        g_hash_table_destroy(summaryview->folder_table);
4228
        summaryview->folder_table = NULL;
4229

    
4230
        return val;
4231
}
4232

    
4233
static gboolean summary_execute_delete_func(GtkTreeModel *model,
4234
                                            GtkTreePath *path,
4235
                                            GtkTreeIter *iter, gpointer data)
4236
{
4237
        SummaryView *summaryview = data;
4238
        MsgInfo *msginfo;
4239

    
4240
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4241

    
4242
        if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
4243
                summaryview->tmp_mlist =
4244
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
4245
        }
4246

    
4247
        return FALSE;
4248
}
4249

    
4250
static gint summary_execute_delete(SummaryView *summaryview)
4251
{
4252
        FolderItem *trash = NULL;
4253
        PrefsAccount *ac;
4254
        gint val = 0;
4255

    
4256
        ac = account_find_from_item_property(summaryview->folder_item);
4257
        if (ac && ac->set_trash_folder && ac->trash_folder)
4258
                trash = folder_find_item_from_identifier(ac->trash_folder);
4259
        if (!trash)
4260
                trash = summaryview->folder_item->folder->trash;
4261
        if (!trash)
4262
                folder_get_default_trash();
4263

    
4264
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
4265
                g_return_val_if_fail(trash != NULL, 0);
4266
        }
4267

    
4268
        /* search deleting messages and execute */
4269
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
4270
                               summary_execute_delete_func, summaryview);
4271

    
4272
        if (!summaryview->tmp_mlist) return 0;
4273

    
4274
        summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist);
4275

    
4276
        if (summaryview->folder_item != trash)
4277
                val = folder_item_move_msgs(trash, summaryview->tmp_mlist);
4278
        else
4279
                val = folder_item_remove_msgs(trash, summaryview->tmp_mlist);
4280

    
4281
        g_slist_free(summaryview->tmp_mlist);
4282
        summaryview->tmp_mlist = NULL;
4283

    
4284
        if (summaryview->folder_item != trash) {
4285
                folder_item_scan(trash);
4286
                folderview_update_item(trash, FALSE);
4287
        }
4288

    
4289
        return val == -1 ? -1 : 0;
4290
}
4291

    
4292
/* thread functions */
4293

    
4294
void summary_thread_build(SummaryView *summaryview)
4295
{
4296
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4297
        GtkTreeStore *store = summaryview->store;
4298
        GtkTreeIter iter, next;
4299
        GtkTreePath *path;
4300
        GSList *mlist;
4301
        GNode *root, *node;
4302
        GHashTable *node_table;
4303
        MsgInfo *msginfo;
4304
        gboolean valid;
4305
        FolderSortKey sort_key = SORT_BY_NONE;
4306
        FolderSortType sort_type = SORT_ASCENDING;
4307
        MsgInfo *selected_msg, *displayed_msg;
4308

    
4309
        if (!summaryview->folder_item)
4310
                return;
4311

    
4312
        summary_lock(summaryview);
4313

    
4314
        debug_print(_("Building threads..."));
4315
        STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
4316
        main_window_cursor_wait(summaryview->mainwin);
4317

    
4318
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4319
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4320
                                        0, 0, NULL, NULL, summaryview);
4321

    
4322
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4323
        displayed_msg = summary_get_msginfo
4324
                (summaryview, summaryview->displayed);
4325

    
4326
        summaryview->folder_item->threaded = TRUE;
4327

    
4328
        mlist = summary_get_msg_list(summaryview);
4329
        root = procmsg_get_thread_tree(mlist);
4330
        node_table = g_hash_table_new(NULL, NULL);
4331
        for (node = root->children; node != NULL; node = node->next) {
4332
                g_hash_table_insert(node_table, node->data, node);
4333
        }
4334

    
4335
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4336
                sort_key = summaryview->folder_item->sort_key;
4337
                sort_type = summaryview->folder_item->sort_type;
4338
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4339
        }
4340

    
4341
        valid = gtk_tree_model_get_iter_first(model, &next);
4342
        while (valid) {
4343
                iter = next;
4344
                valid = gtk_tree_model_iter_next(model, &next);
4345

    
4346
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4347
                node = g_hash_table_lookup(node_table, msginfo);
4348
                if (node) {
4349
                        GNode *cur;
4350
                        GtkTreeIter child;
4351
                        guint tdate;
4352

    
4353
                        for (cur = node->children; cur != NULL;
4354
                             cur = cur->next) {
4355
                                summary_insert_gnode(summaryview, store, &child,
4356
                                                     &iter, NULL, cur);
4357
                        }
4358

    
4359
                        tdate = procmsg_get_thread_date(node);
4360
                        gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1);
4361
                } else
4362
                        gtk_tree_store_remove(store, &iter);
4363
        }
4364

    
4365
        if (sort_key != SORT_BY_NONE)
4366
                summary_sort(summaryview, sort_key, sort_type);
4367

    
4368
        g_hash_table_destroy(node_table);
4369
        g_node_destroy(root);
4370
        g_slist_free(mlist);
4371

    
4372
        if (prefs_common.expand_thread)
4373
                gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4374

    
4375
        if (!summaryview->selected ||
4376
            (summaryview->selected &&
4377
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4378
                if (selected_msg &&
4379
                    gtkut_tree_model_find_by_column_data
4380
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4381
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4382
                }
4383
        } else
4384
                summary_scroll_to_selected(summaryview, TRUE);
4385

    
4386
        if (summaryview->displayed &&
4387
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4388
                if (displayed_msg &&
4389
                    gtkut_tree_model_find_by_column_data
4390
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4391
                        path = gtk_tree_model_get_path(model, &iter);
4392
                        gtk_tree_row_reference_free(summaryview->displayed);
4393
                        summaryview->displayed =
4394
                                gtk_tree_row_reference_new(model, path);
4395
                        gtk_tree_path_free(path);
4396
                } else {
4397
                        messageview_clear(summaryview->messageview);
4398
                        gtk_tree_row_reference_free(summaryview->displayed);
4399
                        summaryview->displayed = NULL;
4400
                }
4401
        }
4402

    
4403
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4404
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4405
                                          0, 0, NULL, NULL, summaryview);
4406

    
4407
        debug_print(_("done.\n"));
4408
        STATUSBAR_POP(summaryview->mainwin);
4409
        main_window_cursor_normal(summaryview->mainwin);
4410

    
4411
        summary_unlock(summaryview);
4412
}
4413

    
4414
static void summary_unthread_node_recursive(SummaryView *summaryview,
4415
                                            GtkTreeIter *iter,
4416
                                            GtkTreeIter *sibling)
4417
{
4418
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4419
        GtkTreeIter iter_, child;
4420
        MsgInfo *msginfo;
4421
        gboolean valid;
4422

    
4423
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4424
        gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_,
4425
                                    NULL, sibling);
4426
        summary_set_row(summaryview, &iter_, msginfo);
4427
        *sibling = iter_;
4428

    
4429
        valid = gtk_tree_model_iter_children(model, &child, iter);
4430
        while (valid) {
4431
                summary_unthread_node_recursive(summaryview, &child, sibling);
4432
                valid = gtk_tree_model_iter_next(model, &child);
4433
        }
4434
}
4435

    
4436
static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter)
4437
{
4438
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4439
        GtkTreeIter child, sibling, next;
4440
        gboolean valid;
4441

    
4442
        sibling = *iter;
4443

    
4444
        valid = gtk_tree_model_iter_children(model, &next, iter);
4445
        while (valid) {
4446
                child = next;
4447
                valid = gtk_tree_model_iter_next(model, &next);
4448
                summary_unthread_node_recursive(summaryview, &child, &sibling);
4449
                gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
4450
        }
4451
}
4452

    
4453
void summary_unthread(SummaryView *summaryview)
4454
{
4455
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4456
        GtkTreeIter iter, next;
4457
        GtkTreePath *path;
4458
        gboolean valid;
4459
        FolderSortKey sort_key = SORT_BY_NONE;
4460
        FolderSortType sort_type = SORT_ASCENDING;
4461
        MsgInfo *selected_msg, *displayed_msg;
4462

    
4463
        summary_lock(summaryview);
4464

    
4465
        debug_print(_("Unthreading..."));
4466
        STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4467
        main_window_cursor_wait(summaryview->mainwin);
4468

    
4469
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4470
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4471
                                        0, 0, NULL, NULL, summaryview);
4472

    
4473
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4474
        displayed_msg = summary_get_msginfo
4475
                (summaryview, summaryview->displayed);
4476

    
4477
        if (summaryview->folder_item)
4478
                summaryview->folder_item->threaded = FALSE;
4479

    
4480
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4481
                sort_key = summaryview->folder_item->sort_key;
4482
                sort_type = summaryview->folder_item->sort_type;
4483
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4484
        }
4485

    
4486
        valid = gtk_tree_model_get_iter_first(model, &next);
4487
        while (valid) {
4488
                iter = next;
4489
                valid = gtk_tree_model_iter_next(model, &next);
4490
                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
4491
                                   S_COL_TDATE, 0, -1);
4492
        }
4493

    
4494
        valid = gtk_tree_model_get_iter_first(model, &next);
4495
        while (valid) {
4496
                iter = next;
4497
                valid = gtk_tree_model_iter_next(model, &next);
4498
                summary_unthread_node(summaryview, &iter);
4499
        }
4500

    
4501
        if (sort_key != SORT_BY_NONE)
4502
                summary_sort(summaryview, sort_key, sort_type);
4503

    
4504
        if (!summaryview->selected ||
4505
            (summaryview->selected &&
4506
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4507
                if (selected_msg &&
4508
                    gtkut_tree_model_find_by_column_data
4509
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4510
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4511
                }
4512
        } else
4513
                summary_scroll_to_selected(summaryview, TRUE);
4514

    
4515
        if (summaryview->displayed &&
4516
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4517
                if (displayed_msg &&
4518
                    gtkut_tree_model_find_by_column_data
4519
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4520
                        path = gtk_tree_model_get_path(model, &iter);
4521
                        gtk_tree_row_reference_free(summaryview->displayed);
4522
                        summaryview->displayed =
4523
                                gtk_tree_row_reference_new(model, path);
4524
                        gtk_tree_path_free(path);
4525
                } else {
4526
                        messageview_clear(summaryview->messageview);
4527
                        gtk_tree_row_reference_free(summaryview->displayed);
4528
                        summaryview->displayed = NULL;
4529
                }
4530
        }
4531

    
4532
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4533
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4534
                                          0, 0, NULL, NULL, summaryview);
4535

    
4536
        debug_print(_("done.\n"));
4537
        STATUSBAR_POP(summaryview->mainwin);
4538
        main_window_cursor_normal(summaryview->mainwin);
4539

    
4540
        summary_unlock(summaryview);
4541
}
4542

    
4543
static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter)
4544
{
4545
        GtkTreeIter child;
4546
        MsgInfo *msginfo;
4547
        gboolean valid;
4548

    
4549
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4550
        if (MSG_IS_INVALID(msginfo->flags))
4551
                return TRUE;
4552

    
4553
        valid = gtk_tree_model_iter_children(model, &child, iter);
4554
        while (valid) {
4555
                if (summary_has_invalid_node(model, &child))
4556
                        return TRUE;
4557
                valid = gtk_tree_model_iter_next(model, &child);
4558
        }
4559

    
4560
        return FALSE;
4561
}
4562

    
4563
static GNode *summary_get_modified_node(SummaryView *summaryview,
4564
                                        GtkTreeIter *iter,
4565
                                        GNode *parent, GNode *sibling)
4566
{
4567
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4568
        GNode *node = NULL, *new_sibling;
4569
        GtkTreeIter child;
4570
        MsgInfo *msginfo;
4571
        gboolean valid;
4572

    
4573
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4574

    
4575
        if (!MSG_IS_INVALID(msginfo->flags)) {
4576
                node = g_node_new(msginfo);
4577
                g_node_insert_after(parent, sibling, node);
4578
                parent = node;
4579
                sibling = NULL;
4580
        } else {
4581
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4582
                                                        msginfo);
4583
                if (summaryview->flt_mlist)
4584
                        summaryview->flt_mlist =
4585
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4586
                procmsg_msginfo_free(msginfo);
4587
        }
4588

    
4589
        valid = gtk_tree_model_iter_children(model, &child, iter);
4590

    
4591
        while (valid) {
4592
                new_sibling = summary_get_modified_node(summaryview, &child,
4593
                                                        parent, sibling);
4594
                if (new_sibling) {
4595
                        sibling = new_sibling;
4596
                        if (!node)
4597
                                node = sibling;
4598
                }
4599
                valid = gtk_tree_model_iter_next(model, &child);
4600
        }
4601

    
4602
        return node;
4603
}
4604

    
4605
#if 0
4606
static gboolean traverse(GNode *node, gpointer data)
4607
{
4608
        gint i;
4609

4610
        if (!node->data)
4611
                return FALSE;
4612
        for (i = 0; i < g_node_depth(node); i++)
4613
                g_print(" ");
4614
        g_print("%s\n", ((MsgInfo *)node->data)->subject);
4615
        return FALSE;
4616
}
4617
#endif
4618

    
4619
static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter,
4620
                                GtkTreeIter *selected)
4621
{
4622
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4623
        MsgInfo *msginfo, *sel_msginfo = NULL;
4624
        GNode *root, *cur;
4625
        GtkTreeIter iter_, sibling;
4626
        GtkTreeIter *sibling_p = NULL;
4627
        GtkTreePath *path, *sel_path;
4628
        gboolean found = FALSE;
4629

    
4630
        if (!gtk_tree_model_iter_has_child(model, iter))
4631
                return;
4632
        if (!summary_has_invalid_node(model, iter))
4633
                return;
4634

    
4635
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4636

    
4637
        if (selected) {
4638
                path = gtk_tree_model_get_path(model, iter);
4639
                sel_path = gtk_tree_model_get_path(model, selected);
4640
                if (gtk_tree_path_compare(path, sel_path) == 0 ||
4641
                    gtk_tree_path_is_ancestor(path, sel_path))
4642
                        gtk_tree_model_get(model, selected,
4643
                                           S_COL_MSG_INFO, &sel_msginfo, -1);
4644
                gtk_tree_path_free(sel_path);
4645
                gtk_tree_path_free(path);
4646
        }
4647

    
4648
        root = g_node_new(NULL);
4649
        summary_get_modified_node(summaryview, iter, root, NULL);
4650

    
4651
        /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */
4652

    
4653
        sibling = *iter;
4654
        if (gtk_tree_model_iter_next(model, &sibling))
4655
                sibling_p = &sibling;
4656

    
4657
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
4658

    
4659
        for (cur = root->children; cur != NULL; cur = cur->next) {
4660
                summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model),
4661
                                            &iter_, NULL, sibling_p, cur);
4662
                if (summaryview->folder_item->threaded &&
4663
                    prefs_common.expand_thread) {
4664
                        path = gtk_tree_model_get_path(model, &iter_);
4665
                        gtk_tree_view_expand_row
4666
                                (GTK_TREE_VIEW(summaryview->treeview),
4667
                                 path, TRUE);
4668
                        gtk_tree_path_free(path);
4669
                }
4670
                if (sel_msginfo && !found) {
4671
                        found = gtkut_tree_model_find_by_column_data
4672
                                (model, selected, &iter_,
4673
                                 S_COL_MSG_INFO, sel_msginfo);
4674
                }
4675
        }
4676

    
4677
        g_node_destroy(root);
4678

    
4679
        summaryview->folder_item->cache_dirty = TRUE;
4680
}
4681

    
4682
static void summary_modify_threads(SummaryView *summaryview)
4683
{
4684
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4685
        GtkTreeIter iter, next, selected, new_selected;
4686
        GtkTreeIter *selected_p = NULL;
4687
        GtkTreePath *prev_path = NULL;
4688
        gboolean valid, has_selection;
4689

    
4690
        summary_lock(summaryview);
4691

    
4692
        debug_print("Modifying threads for execution...");
4693

    
4694
        g_signal_handlers_block_by_func(summaryview->selection,
4695
                                        summary_selection_changed, summaryview);
4696

    
4697
        has_selection = gtkut_tree_row_reference_get_iter
4698
                (model, summaryview->selected, &selected);
4699
        if (has_selection) {
4700
                prev_path = gtk_tree_row_reference_get_path
4701
                        (summaryview->selected);
4702
                if (summary_find_nearest_msg(summaryview, &next, &selected)) {
4703
                        selected = next;
4704
                        selected_p = &selected;
4705
                } else
4706
                        has_selection = FALSE;
4707
        }
4708

    
4709
        valid = gtk_tree_model_get_iter_first(model, &next);
4710
        while (valid) {
4711
                iter = next;
4712
                valid = gtk_tree_model_iter_next(model, &next);
4713
                summary_modify_node(summaryview, &iter, selected_p);
4714
        }
4715

    
4716
        g_signal_handlers_unblock_by_func(summaryview->selection,
4717
                                          summary_selection_changed,
4718
                                          summaryview);
4719

    
4720
        if (summaryview->folder_item->cache_dirty)
4721
                summary_selection_list_free(summaryview);
4722

    
4723
        if (has_selection &&
4724
            !gtk_tree_row_reference_valid(summaryview->selected)) {
4725
                if (prev_path &&
4726
                    gtk_tree_model_get_iter(model, &new_selected, prev_path))
4727
                        selected = new_selected;
4728
                summary_select_row(summaryview, &selected, FALSE, FALSE);
4729
        }
4730
        gtk_tree_path_free(prev_path);
4731

    
4732
        debug_print("done.\n");
4733

    
4734
        summary_unlock(summaryview);
4735
}
4736

    
4737
void summary_expand_threads(SummaryView *summaryview)
4738
{
4739
        gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4740
}
4741

    
4742
void summary_collapse_threads(SummaryView *summaryview)
4743
{
4744
        gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview));
4745
}
4746

    
4747
static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path,
4748
                                    GtkTreeIter *iter, gpointer data)
4749
{
4750
        SummaryView *summaryview = (SummaryView *)data;
4751
        MsgInfo *msginfo;
4752
        FilterInfo *fltinfo;
4753

    
4754
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4755

    
4756
        summaryview->flt_count++;
4757
        {
4758
                gchar msg[1024];
4759
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4760
                           summaryview->flt_count, summaryview->flt_total);
4761
                STATUSBAR_POP(summaryview->mainwin);
4762
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4763
                if ((summaryview->flt_count % 100) == 0) {
4764
                        GTK_EVENTS_FLUSH();
4765
                }
4766
        }
4767

    
4768
        fltinfo = filter_info_new();
4769
        fltinfo->flags = msginfo->flags;
4770
        filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
4771
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4772
            fltinfo->actions[FLT_ACTION_COPY] ||
4773
            fltinfo->actions[FLT_ACTION_DELETE] ||
4774
            fltinfo->actions[FLT_ACTION_EXEC] ||
4775
            fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
4776
            fltinfo->actions[FLT_ACTION_MARK] ||
4777
            fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
4778
            fltinfo->actions[FLT_ACTION_MARK_READ] ||
4779
            fltinfo->actions[FLT_ACTION_FORWARD] ||
4780
            fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
4781
            fltinfo->actions[FLT_ACTION_REDIRECT])
4782
                summaryview->filtered++;
4783

    
4784
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4785
                msginfo->flags = fltinfo->flags;
4786
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4787
                summaryview->folder_item->mark_dirty = TRUE;
4788
                summary_set_row(summaryview, iter, msginfo);
4789
                if (MSG_IS_IMAP(msginfo->flags)) {
4790
                        if (fltinfo->actions[FLT_ACTION_MARK])
4791
                                imap_msg_set_perm_flags(msginfo, MSG_MARKED);
4792
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4793
                                imap_msg_unset_perm_flags(msginfo,
4794
                                                          MSG_NEW|MSG_UNREAD);
4795
                }
4796
        }
4797

    
4798
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4799
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4800
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4801
                summary_delete_row(summaryview, iter);
4802

    
4803
        filter_info_free(fltinfo);
4804

    
4805
        return FALSE;
4806
}
4807

    
4808
static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path,
4809
                                         GtkTreeIter *iter, gpointer data)
4810
{
4811
        SummaryView *summaryview = (SummaryView *)data;
4812
        MsgInfo *msginfo;
4813
        FilterInfo *fltinfo;
4814

    
4815
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4816

    
4817
        summaryview->flt_count++;
4818
        {
4819
                gchar msg[1024];
4820
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4821
                           summaryview->flt_count, summaryview->flt_total);
4822
                STATUSBAR_POP(summaryview->mainwin);
4823
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4824
                if ((summaryview->flt_count % 100) == 0) {
4825
                        GTK_EVENTS_FLUSH();
4826
                }
4827
        }
4828

    
4829
        fltinfo = filter_info_new();
4830
        fltinfo->flags = msginfo->flags;
4831
        filter_apply_msginfo(summaryview->junk_fltlist, msginfo, fltinfo);
4832

    
4833
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4834
            fltinfo->actions[FLT_ACTION_COPY] ||
4835
            fltinfo->actions[FLT_ACTION_DELETE] ||
4836
            fltinfo->actions[FLT_ACTION_MARK_READ])
4837
                summaryview->filtered++;
4838
        else if (fltinfo->error == FLT_ERROR_EXEC_FAILED ||
4839
                 fltinfo->last_exec_exit_status >= 3) {
4840
                if (summaryview->flt_count == 1) {
4841
                        g_warning("summary_filter_junk_func: junk filter command returned %d", fltinfo->last_exec_exit_status);
4842
                        alertpanel_error
4843
                                (_("Execution of the junk filter command failed.\n"
4844
                                   "Please check the junk mail control setting."));
4845
                }
4846
                return TRUE;
4847
        }
4848

    
4849
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4850
                msginfo->flags = fltinfo->flags;
4851
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4852
                summaryview->folder_item->mark_dirty = TRUE;
4853
                summary_set_row(summaryview, iter, msginfo);
4854
                if (MSG_IS_IMAP(msginfo->flags)) {
4855
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4856
                                imap_msg_unset_perm_flags(msginfo,
4857
                                                          MSG_NEW|MSG_UNREAD);
4858
                }
4859
        }
4860

    
4861
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4862
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4863
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4864
                summary_delete_row(summaryview, iter);
4865

    
4866
        filter_info_free(fltinfo);
4867

    
4868
        return FALSE;
4869
}
4870

    
4871
static void summary_filter_real(SummaryView *summaryview,
4872
                                GtkTreeModelForeachFunc func,
4873
                                gboolean selected_only)
4874
{
4875
        GList *rows;
4876
        FolderSortKey sort_key;
4877
        FolderSortType sort_type;
4878

    
4879
        if (!summaryview->folder_item) return;
4880

    
4881
        if (summary_is_locked(summaryview)) return;
4882
        summary_lock(summaryview);
4883

    
4884
        STATUSBAR_POP(summaryview->mainwin);
4885

    
4886
        debug_print(_("filtering..."));
4887
        STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4888
        main_window_cursor_wait(summaryview->mainwin);
4889
        GTK_EVENTS_FLUSH();
4890

    
4891
        sort_key = summaryview->folder_item->sort_key;
4892
        sort_type = summaryview->folder_item->sort_type;
4893
        if (sort_key != SORT_BY_NONE)
4894
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4895

    
4896
        summaryview->filtered = 0;
4897
        summaryview->flt_count = 0;
4898

    
4899
        if (selected_only) {
4900
                rows = summary_get_selected_rows(summaryview);
4901
                summaryview->flt_total = g_list_length(rows);
4902

    
4903
                gtk_tree_selection_selected_foreach
4904
                        (summaryview->selection,
4905
                         (GtkTreeSelectionForeachFunc)func,
4906
                         summaryview);
4907
        } else {
4908
                summaryview->flt_total = summaryview->folder_item->total;
4909
                gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4910
                                       func, summaryview);
4911
        }
4912

    
4913
        if (sort_key != SORT_BY_NONE)
4914
                summary_sort(summaryview, sort_key, sort_type);
4915

    
4916
        summary_unlock(summaryview);
4917

    
4918
        if (prefs_common.immediate_exec)
4919
                summary_execute(summaryview);
4920
        else
4921
                summary_status_show(summaryview);
4922

    
4923
        folderview_update_all_updated(FALSE);
4924

    
4925
        debug_print(_("done.\n"));
4926
        STATUSBAR_POP(summaryview->mainwin);
4927
        main_window_cursor_normal(summaryview->mainwin);
4928

    
4929
        if (summaryview->filtered > 0) {
4930
                gchar result_msg[BUFFSIZE];
4931
                g_snprintf(result_msg, sizeof(result_msg),
4932
                           _("%d message(s) have been filtered."),
4933
                           summaryview->filtered);
4934
                STATUSBAR_PUSH(summaryview->mainwin, result_msg);
4935
        }
4936
        summaryview->filtered = 0;
4937
        summaryview->flt_count = 0;
4938
        summaryview->flt_total = 0;
4939
}
4940

    
4941
void summary_filter(SummaryView *summaryview, gboolean selected_only)
4942
{
4943
        if (prefs_common.fltlist)
4944
                summary_filter_real(summaryview, summary_filter_func,
4945
                                    selected_only);
4946
}
4947

    
4948
void summary_filter_junk(SummaryView *summaryview, gboolean selected_only)
4949
{
4950
        FilterRule *rule;
4951
        GSList junk_fltlist = {NULL, NULL};
4952
        FolderItem *item = summaryview->folder_item;
4953
        FolderItem *junk = NULL;
4954

    
4955
        if (!item)
4956
                return;
4957

    
4958
        if (item->folder)
4959
                junk = folder_get_junk(item->folder);
4960
        rule = filter_junk_rule_create(NULL, junk, TRUE);
4961
        if (rule) {
4962
                junk_fltlist.data = rule;
4963
                summaryview->junk_fltlist = &junk_fltlist;
4964
                summary_filter_real(summaryview, summary_filter_junk_func,
4965
                                    selected_only);
4966
                summaryview->junk_fltlist = NULL;
4967
                filter_rule_free(rule);
4968
        }
4969
}
4970

    
4971
void summary_filter_open(SummaryView *summaryview, FilterCreateType type)
4972
{
4973
        GtkTreeIter iter;
4974
        MsgInfo *msginfo = NULL;
4975
        gchar *header = NULL;
4976
        gchar *key = NULL;
4977

    
4978
        if (!summaryview->selected) return;
4979
        if (!gtkut_tree_row_reference_get_iter
4980
                (GTK_TREE_MODEL(summaryview->store),
4981
                 summaryview->selected, &iter))
4982
                return;
4983

    
4984
        GET_MSG_INFO(msginfo, &iter);
4985
        if (!msginfo) return;
4986

    
4987
        filter_get_keyword_from_msg(msginfo, &header, &key, type);
4988
        prefs_filter_open(msginfo, header, key);
4989

    
4990
        g_free(header);
4991
        g_free(key);
4992
}
4993

    
4994
static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path,
4995
                              GtkTreeIter *iter, gpointer data)
4996
{
4997
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
4998
        FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0};
4999
        FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0};
5000
        FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0};
5001
        SummaryView *summaryview = (SummaryView *)data;
5002
        MsgInfo *msginfo;
5003
        FilterInfo *fltinfo;
5004
        gchar *file;
5005
        gchar *junk_id = NULL;
5006
        gint ret;
5007

    
5008
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
5009
        file = procmsg_get_message_file(msginfo);
5010
        g_return_if_fail(file != NULL);
5011

    
5012
        if (summaryview->to_folder)
5013
                junk_id = folder_item_get_identifier(summaryview->to_folder);
5014

    
5015
        action1.str_value = prefs_common.junk_learncmd;
5016
        action2.str_value = junk_id;
5017

    
5018
        rule.action_list = g_slist_append(rule.action_list, &action1);
5019
        if (junk_id)
5020
                rule.action_list = g_slist_append(rule.action_list, &action2);
5021
        if (prefs_common.mark_junk_as_read)
5022
                rule.action_list = g_slist_append(rule.action_list, &action3);
5023

    
5024
        fltinfo = filter_info_new();
5025
        fltinfo->flags = msginfo->flags;
5026

    
5027
        ret = filter_action_exec(&rule, msginfo, file, fltinfo);
5028

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

    
5052
        filter_info_free(fltinfo);
5053
        g_slist_free(rule.action_list);
5054
        g_free(junk_id);
5055
        g_free(file);