Statistics
| Revision:

root / src / summaryview.c @ 1978

History | View | Annotate | Download (167 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2007 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
88
#define STATUSBAR_PUSH(mainwin, str) \
89
{ \
90
        gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
91
                           mainwin->summaryview_cid, str); \
92
        gtkut_widget_draw_now(mainwin->statusbar); \
93
}
94
95
#define STATUSBAR_POP(mainwin) \
96
{ \
97
        gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
98
                          mainwin->summaryview_cid); \
99
}
100
101
#define GET_MSG_INFO(msginfo, iter__) \
102
{ \
103
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter__, \
104
                           S_COL_MSG_INFO, &msginfo, -1); \
105
}
106
107
#define SORT_BLOCK(key)                                                         \
108
        if (summaryview->folder_item->sort_key == key) {                 \
109
                sort_key = key;                                                 \
110
                sort_type = summaryview->folder_item->sort_type;         \
111
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING); \
112
        }
113
114
#define SORT_UNBLOCK(key)                                        \
115
        if (sort_key == key)                                        \
116
                summary_sort(summaryview, sort_key, sort_type);
117
118
#ifdef G_OS_WIN32
119
#  define SUMMARY_COL_MARK_WIDTH        23
120
#  define SUMMARY_COL_UNREAD_WIDTH        26
121
#  define SUMMARY_COL_MIME_WIDTH        19
122
#else
123
#  define SUMMARY_COL_MARK_WIDTH        21
124
#  define SUMMARY_COL_UNREAD_WIDTH        24
125
#  define SUMMARY_COL_MIME_WIDTH        17
126
#endif
127
128
static GdkPixbuf *mark_pixbuf;
129
static GdkPixbuf *deleted_pixbuf;
130
131
static GdkPixbuf *mail_pixbuf;
132
static GdkPixbuf *new_pixbuf;
133
static GdkPixbuf *unread_pixbuf;
134
static GdkPixbuf *replied_pixbuf;
135
static GdkPixbuf *forwarded_pixbuf;
136
137
static GdkPixbuf *clip_pixbuf;
138
139
static void summary_clear_list_full        (SummaryView                *summaryview,
140
                                         gboolean                 is_refresh);
141
142
static GList *summary_get_selected_rows        (SummaryView                *summaryview);
143
static void summary_selection_list_free        (SummaryView                *summaryview);
144
145
static void summary_update_msg_list        (SummaryView                *summaryview);
146
147
static void summary_msgid_table_create        (SummaryView                *summaryview);
148
static void summary_msgid_table_destroy        (SummaryView                *summaryview);
149
150
static void summary_set_menu_sensitive        (SummaryView                *summaryview);
151
152
static void summary_scroll_to_selected        (SummaryView                *summaryview,
153
                                         gboolean                 align_center);
154
155
static MsgInfo *summary_get_msginfo        (SummaryView                *summaryview,
156
                                         GtkTreeRowReference        *row);
157
static guint summary_get_msgnum                (SummaryView                *summaryview,
158
                                         GtkTreeRowReference        *row);
159
160
static gboolean summary_find_prev_msg        (SummaryView                *summaryview,
161
                                         GtkTreeIter                *prev,
162
                                         GtkTreeIter                *iter);
163
static gboolean summary_find_next_msg        (SummaryView                *summaryview,
164
                                         GtkTreeIter                *next,
165
                                         GtkTreeIter                *iter);
166
167
static gboolean summary_find_nearest_msg(SummaryView                *summaryview,
168
                                         GtkTreeIter                *target,
169
                                         GtkTreeIter                *iter);
170
171
static gboolean summary_find_prev_flagged_msg
172
                                        (SummaryView        *summaryview,
173
                                         GtkTreeIter        *prev,
174
                                         GtkTreeIter        *iter,
175
                                         MsgPermFlags         flags,
176
                                         gboolean         start_from_prev);
177
static gboolean summary_find_next_flagged_msg
178
                                        (SummaryView        *summaryview,
179
                                         GtkTreeIter        *next,
180
                                         GtkTreeIter        *iter,
181
                                         MsgPermFlags         flags,
182
                                         gboolean         start_from_next);
183
184
static gboolean summary_find_msg_by_msgnum
185
                                        (SummaryView                *summaryview,
186
                                         guint                         msgnum,
187
                                         GtkTreeIter                *found);
188
189
static void summary_update_display_state(SummaryView                *summaryview,
190
                                         guint                         disp_msgnum,
191
                                         guint                         sel_msgnum);
192
193
static void summary_update_status        (SummaryView                *summaryview);
194
195
/* display functions */
196
static void summary_status_show                (SummaryView                *summaryview);
197
static void summary_set_tree_model_from_list
198
                                        (SummaryView                *summaryview,
199
                                         GSList                        *mlist);
200
static gboolean summary_row_is_displayed(SummaryView                *summaryview,
201
                                         GtkTreeIter                *iter);
202
static void summary_display_msg                (SummaryView                *summaryview,
203
                                         GtkTreeIter                *iter);
204
static void summary_display_msg_full        (SummaryView                *summaryview,
205
                                         GtkTreeIter                *iter,
206
                                         gboolean                 new_window,
207
                                         gboolean                 all_headers,
208
                                         gboolean                 redisplay);
209
210
static void summary_activate_selected        (SummaryView                *summaryview);
211
212
/* message handling */
213
static void summary_mark_row                (SummaryView                *summaryview,
214
                                         GtkTreeIter                *iter);
215
static void summary_mark_row_as_read        (SummaryView                *summaryview,
216
                                         GtkTreeIter                *iter);
217
static void summary_mark_row_as_unread        (SummaryView                *summaryview,
218
                                         GtkTreeIter                *iter);
219
static void summary_delete_row                (SummaryView                *summaryview,
220
                                         GtkTreeIter                *iter);
221
static void summary_unmark_row                (SummaryView                *summaryview,
222
                                         GtkTreeIter                *iter);
223
static void summary_move_row_to                (SummaryView                *summaryview,
224
                                         GtkTreeIter                *iter,
225
                                         FolderItem                *to_folder);
226
static void summary_copy_row_to                (SummaryView                *summaryview,
227
                                         GtkTreeIter                *iter,
228
                                         FolderItem                *to_folder);
229
230
static void summary_remove_invalid_messages
231
                                        (SummaryView                *summaryview);
232
233
static gint summary_execute_move        (SummaryView                *summaryview);
234
static gint summary_execute_copy        (SummaryView                *summaryview);
235
static gint summary_execute_delete        (SummaryView                *summaryview);
236
237
static void summary_modify_threads        (SummaryView                *summaryview);
238
239
static void summary_colorlabel_menu_item_activate_cb
240
                                        (GtkWidget        *widget,
241
                                         gpointer         data);
242
static void summary_colorlabel_menu_item_activate_item_cb
243
                                        (GtkMenuItem        *label_menu_item,
244
                                         gpointer         data);
245
static void summary_colorlabel_menu_create
246
                                        (SummaryView        *summaryview);
247
248
static GtkWidget *summary_tree_view_create
249
                                        (SummaryView        *summaryview);
250
251
/* callback functions */
252
static gboolean summary_toggle_pressed        (GtkWidget                *eventbox,
253
                                         GdkEventButton                *event,
254
                                         SummaryView                *summaryview);
255
static gboolean summary_button_pressed        (GtkWidget                *treeview,
256
                                         GdkEventButton                *event,
257
                                         SummaryView                *summaryview);
258
static gboolean summary_button_released        (GtkWidget                *treeview,
259
                                         GdkEventButton                *event,
260
                                         SummaryView                *summaryview);
261
static gboolean summary_key_pressed        (GtkWidget                *treeview,
262
                                         GdkEventKey                *event,
263
                                         SummaryView                *summaryview);
264
265
static void summary_row_expanded        (GtkTreeView                *treeview,
266
                                         GtkTreeIter                *iter,
267
                                         GtkTreePath                *path,
268
                                         SummaryView                *summaryview);
269
static void summary_row_collapsed        (GtkTreeView                *treeview,
270
                                         GtkTreeIter                *iter,
271
                                         GtkTreePath                *path,
272
                                         SummaryView                *summaryview);
273
274
static void summary_columns_changed        (GtkTreeView                *treeview,
275
                                         SummaryView                *summaryview);
276
277
static gboolean summary_select_func        (GtkTreeSelection        *treeview,
278
                                         GtkTreeModel                *model,
279
                                         GtkTreePath                *path,
280
                                         gboolean                 cur_selected,
281
                                         gpointer                 data);
282
283
static void summary_selection_changed        (GtkTreeSelection        *selection,
284
                                         SummaryView                *summaryview);
285
286
static void summary_col_resized                (GtkWidget                *widget,
287
                                         GtkAllocation                *allocation,
288
                                         SummaryView                *summaryview);
289
290
static void summary_reply_cb                (SummaryView                *summaryview,
291
                                         guint                         action,
292
                                         GtkWidget                *widget);
293
static void summary_show_all_header_cb        (SummaryView                *summaryview,
294
                                         guint                         action,
295
                                         GtkWidget                *widget);
296
297
static void summary_add_address_cb        (SummaryView                *summaryview,
298
                                         guint                         action,
299
                                         GtkWidget                *widget);
300
static void summary_create_filter_cb        (SummaryView                *summaryview,
301
                                         guint                         action,
302
                                         GtkWidget                *widget);
303
304
static void summary_column_clicked        (GtkWidget                *button,
305
                                         SummaryView                *summaryview);
306
307
static void summary_drag_begin                (GtkWidget          *widget,
308
                                         GdkDragContext          *drag_context,
309
                                         SummaryView          *summaryview);
310
static void summary_drag_end                (GtkWidget          *widget,
311
                                         GdkDragContext          *drag_context,
312
                                         SummaryView          *summaryview);
313
static void summary_drag_data_get       (GtkWidget        *widget,
314
                                         GdkDragContext   *drag_context,
315
                                         GtkSelectionData *selection_data,
316
                                         guint             info,
317
                                         guint             time,
318
                                         SummaryView      *summaryview);
319
320
/* custom compare functions for sorting */
321
static gint summary_cmp_by_mark                (GtkTreeModel                *model,
322
                                         GtkTreeIter                *a,
323
                                         GtkTreeIter                *b,
324
                                         gpointer                 data);
325
static gint summary_cmp_by_unread        (GtkTreeModel                *model,
326
                                         GtkTreeIter                *a,
327
                                         GtkTreeIter                *b,
328
                                         gpointer                 data);
329
static gint summary_cmp_by_mime                (GtkTreeModel                *model,
330
                                         GtkTreeIter                *a,
331
                                         GtkTreeIter                *b,
332
                                         gpointer                 data);
333
static gint summary_cmp_by_num                (GtkTreeModel                *model,
334
                                         GtkTreeIter                *a,
335
                                         GtkTreeIter                *b,
336
                                         gpointer                 data);
337
static gint summary_cmp_by_size                (GtkTreeModel                *model,
338
                                         GtkTreeIter                *a,
339
                                         GtkTreeIter                *b,
340
                                         gpointer                 data);
341
static gint summary_cmp_by_date                (GtkTreeModel                *model,
342
                                         GtkTreeIter                *a,
343
                                         GtkTreeIter                *b,
344
                                         gpointer                 data);
345
static gint summary_cmp_by_thread_date        (GtkTreeModel                *model,
346
                                         GtkTreeIter                *a,
347
                                         GtkTreeIter                *b,
348
                                         gpointer                 data);
349
static gint summary_cmp_by_from                (GtkTreeModel                *model,
350
                                         GtkTreeIter                *a,
351
                                         GtkTreeIter                *b,
352
                                         gpointer                 data);
353
static gint summary_cmp_by_label        (GtkTreeModel                *model,
354
                                         GtkTreeIter                *a,
355
                                         GtkTreeIter                *b,
356
                                         gpointer                 data);
357
static gint summary_cmp_by_to                (GtkTreeModel                *model,
358
                                         GtkTreeIter                *a,
359
                                         GtkTreeIter                *b,
360
                                         gpointer                 data);
361
static gint summary_cmp_by_subject        (GtkTreeModel                *model,
362
                                         GtkTreeIter                *a,
363
                                         GtkTreeIter                *b,
364
                                         gpointer                 data);
365
366
367
/* must be synched with FolderSortKey */
368
static SummaryColumnType sort_key_to_col[] = {
369
        -1,
370
        S_COL_NUMBER,
371
        S_COL_SIZE,
372
        S_COL_DATE,
373
        S_COL_TDATE,
374
        S_COL_FROM,
375
        S_COL_SUBJECT,
376
        -1,
377
        S_COL_LABEL,
378
        S_COL_MARK,
379
        S_COL_UNREAD,
380
        S_COL_MIME,
381
        S_COL_TO
382
};
383
384
/* must be synched with SummaryColumnType */
385
static FolderSortKey col_to_sort_key[] = {
386
        SORT_BY_MARK,
387
        SORT_BY_UNREAD,
388
        SORT_BY_MIME,
389
        SORT_BY_SUBJECT,
390
        SORT_BY_FROM,
391
        SORT_BY_DATE,
392
        SORT_BY_SIZE,
393
        SORT_BY_NUMBER,
394
        SORT_BY_TO
395
};
396
397
enum
398
{
399
        DRAG_TYPE_TEXT,
400
        DRAG_TYPE_RFC822,
401
        DRAG_TYPE_URI_LIST,
402
403
        N_DRAG_TYPES
404
};
405
406
static GtkTargetEntry summary_drag_types[] =
407
{
408
        {"text/plain", GTK_TARGET_SAME_APP, DRAG_TYPE_TEXT},
409
        {"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
410
        {"text/uri-list", 0, DRAG_TYPE_URI_LIST}
411
};
412
413
static GtkItemFactoryEntry summary_popup_entries[] =
414
{
415
        {N_("/_Reply"),                        NULL, summary_reply_cb,        COMPOSE_REPLY, NULL},
416
        {N_("/Repl_y to"),                NULL, NULL,                0, "<Branch>"},
417
        {N_("/Repl_y to/_all"),                NULL, summary_reply_cb,        COMPOSE_REPLY_TO_ALL, NULL},
418
        {N_("/Repl_y to/_sender"),        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_SENDER, NULL},
419
        {N_("/Repl_y to/mailing _list"),
420
                                        NULL, summary_reply_cb,        COMPOSE_REPLY_TO_LIST, NULL},
421
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
422
        {N_("/_Forward"),                NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
423
        {N_("/For_ward as attachment"),        NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
424
        {N_("/Redirec_t"),                NULL, summary_reply_cb, COMPOSE_REDIRECT, NULL},
425
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
426
        {N_("/M_ove..."),                NULL, summary_move_to,        0, NULL},
427
        {N_("/_Copy..."),                NULL, summary_copy_to,        0, NULL},
428
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
429
        {N_("/_Mark"),                        NULL, NULL,                0, "<Branch>"},
430
        {N_("/_Mark/_Mark"),                NULL, summary_mark,        0, NULL},
431
        {N_("/_Mark/_Unmark"),                NULL, summary_unmark,        0, NULL},
432
        {N_("/_Mark/---"),                NULL, NULL,                0, "<Separator>"},
433
        {N_("/_Mark/Mark as unr_ead"),        NULL, summary_mark_as_unread, 0, NULL},
434
        {N_("/_Mark/Mark as rea_d"),
435
                                        NULL, summary_mark_as_read, 0, NULL},
436
        {N_("/_Mark/Mark _thread as read"),
437
                                        NULL, summary_mark_thread_as_read, 0, NULL},
438
        {N_("/_Mark/Mark all _read"),        NULL, summary_mark_all_read, 0, NULL},
439
        {N_("/Color la_bel"),                NULL, NULL,                0, NULL},
440
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
441
        {N_("/_Delete"),                NULL, summary_delete,        0, NULL},
442
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
443
        {N_("/Set as _junk mail"),        NULL, summary_junk,        0, NULL},
444
        {N_("/Set as not j_unk mail"),        NULL, summary_not_junk,        0, NULL},
445
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
446
        {N_("/Re-_edit"),                NULL, summary_reedit,   0, NULL},
447
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
448
        {N_("/Add sender to address boo_k..."),
449
                                        NULL, summary_add_address_cb, 0, NULL},
450
        {N_("/Create f_ilter rule"),        NULL, NULL,                0, "<Branch>"},
451
        {N_("/Create f_ilter rule/_Automatically"),
452
                                        NULL, summary_create_filter_cb, FLT_BY_AUTO, NULL},
453
        {N_("/Create f_ilter rule/by _From"),
454
                                        NULL, summary_create_filter_cb, FLT_BY_FROM, NULL},
455
        {N_("/Create f_ilter rule/by _To"),
456
                                        NULL, summary_create_filter_cb, FLT_BY_TO, NULL},
457
        {N_("/Create f_ilter rule/by _Subject"),
458
                                        NULL, summary_create_filter_cb, FLT_BY_SUBJECT, NULL},
459
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
460
        {N_("/_View"),                        NULL, NULL,                0, "<Branch>"},
461
        {N_("/_View/Open in new _window"),
462
                                        NULL, summary_open_msg,        0, NULL},
463
        {N_("/_View/Mess_age source"),        NULL, summary_view_source, 0, NULL},
464
        {N_("/_View/All _headers"),        NULL, summary_show_all_header_cb, 0, "<ToggleItem>"},
465
        {N_("/---"),                        NULL, NULL,                0, "<Separator>"},
466
        {N_("/_Print..."),                NULL, summary_print,        0, NULL}
467
};
468
469
SummaryView *summary_create(void)
470
{
471
        SummaryView *summaryview;
472
        GtkWidget *vbox;
473
        GtkWidget *scrolledwin;
474
        GtkWidget *treeview;
475
        GtkTreeStore *store;
476
        GtkTreeSelection *selection;
477
        GtkWidget *hseparator;
478
        GtkWidget *hbox;
479
        GtkWidget *statlabel_folder;
480
        GtkWidget *statlabel_select;
481
        GtkWidget *statlabel_msgs;
482
        GtkWidget *toggle_eventbox;
483
        GtkWidget *toggle_arrow;
484
        GtkWidget *popupmenu;
485
        GtkItemFactory *popupfactory;
486
        gint n_entries;
487
        GList *child;
488
489
        debug_print(_("Creating summary view...\n"));
490
        summaryview = g_new0(SummaryView, 1);
491
492
        vbox = gtk_vbox_new(FALSE, 1);
493
494
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
495
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
496
                                       GTK_POLICY_AUTOMATIC,
497
                                       GTK_POLICY_ALWAYS);
498
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
499
                                            GTK_SHADOW_IN);
500
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
501
        gtk_widget_set_size_request(vbox,
502
                                    prefs_common.summaryview_width,
503
                                    prefs_common.summaryview_height);
504
505
        treeview = summary_tree_view_create(summaryview);
506
        gtk_container_add(GTK_CONTAINER(scrolledwin), treeview);
507
508
        store = GTK_TREE_STORE
509
                (gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)));
510
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
511
512
        /* create status label */
513
        hseparator = gtk_hseparator_new();
514
        gtk_box_pack_end(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0);
515
516
        hbox = gtk_hbox_new(FALSE, 0);
517
        gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
518
519
        statlabel_folder = gtk_label_new("");
520
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
521
        statlabel_select = gtk_label_new("");
522
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 12);
523
524
        /* toggle view button */
525
        toggle_eventbox = gtk_event_box_new();
526
        gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
527
        toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
528
        gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
529
        g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
530
                         G_CALLBACK(summary_toggle_pressed), summaryview);
531
532
        statlabel_msgs = gtk_label_new("");
533
        gtk_misc_set_alignment(GTK_MISC(statlabel_msgs), 1, 0.5);
534
#if GTK_CHECK_VERSION(2, 6, 0)
535
        gtk_label_set_ellipsize(GTK_LABEL(statlabel_msgs),
536
                                PANGO_ELLIPSIZE_START);
537
#endif
538
        gtk_box_pack_start(GTK_BOX(hbox), statlabel_msgs, TRUE, TRUE, 2);
539
540
        /* create popup menu */
541
        n_entries = sizeof(summary_popup_entries) /
542
                sizeof(summary_popup_entries[0]);
543
        popupmenu = menu_create_items(summary_popup_entries, n_entries,
544
                                      "<SummaryView>", &popupfactory,
545
                                      summaryview);
546
547
        summaryview->vbox = vbox;
548
        summaryview->scrolledwin = scrolledwin;
549
        summaryview->treeview = treeview;
550
        summaryview->store = store;
551
        summaryview->selection = selection;
552
        summaryview->hseparator = hseparator;
553
        summaryview->hbox = hbox;
554
        summaryview->statlabel_folder = statlabel_folder;
555
        summaryview->statlabel_select = statlabel_select;
556
        summaryview->statlabel_msgs = statlabel_msgs;
557
        summaryview->toggle_eventbox = toggle_eventbox;
558
        summaryview->toggle_arrow = toggle_arrow;
559
        summaryview->popupmenu = popupmenu;
560
        summaryview->popupfactory = popupfactory;
561
        summaryview->lock_count = 0;
562
563
        summaryview->reedit_menuitem =
564
                gtk_item_factory_get_widget(popupfactory, "/Re-edit");
565
        child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
566
                            summaryview->reedit_menuitem);
567
        summaryview->reedit_separator = GTK_WIDGET(child->next->data);
568
569
        summaryview->junk_menuitem =
570
                gtk_item_factory_get_widget(popupfactory, "/Set as junk mail");
571
        summaryview->nojunk_menuitem =
572
                gtk_item_factory_get_widget(popupfactory,
573
                                            "/Set as not junk mail");
574
        child = g_list_find(GTK_MENU_SHELL(popupmenu)->children,
575
                            summaryview->nojunk_menuitem);
576
        summaryview->junk_separator = GTK_WIDGET(child->next->data);
577
578
        gtk_widget_show_all(vbox);
579
580
        return summaryview;
581
}
582
583
void summary_init(SummaryView *summaryview)
584
{
585
        GtkWidget *pixmap;
586
        PangoFontDescription *font_desc;
587
        gint size;
588
589
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MARK,
590
                         &mark_pixbuf);
591
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_DELETED,
592
                         &deleted_pixbuf);
593
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_MAIL_SMALL,
594
                         &mail_pixbuf);
595
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_NEW,
596
                         &new_pixbuf);
597
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_UNREAD,
598
                         &unread_pixbuf);
599
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_REPLIED,
600
                         &replied_pixbuf);
601
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_FORWARDED,
602
                         &forwarded_pixbuf);
603
        stock_pixbuf_gdk(summaryview->treeview, STOCK_PIXMAP_CLIP,
604
                         &clip_pixbuf);
605
606
        font_desc = pango_font_description_new();
607
        size = pango_font_description_get_size
608
                (summaryview->statlabel_folder->style->font_desc);
609
        pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
610
        gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
611
        gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
612
        gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
613
        pango_font_description_free(font_desc);
614
615
        pixmap = stock_pixbuf_widget(summaryview->hbox,
616
                                     STOCK_PIXMAP_DIR_OPEN);
617
        gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
618
        gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
619
        gtk_widget_show(pixmap);
620
621
        summary_clear_list(summaryview);
622
        summary_set_column_order(summaryview);
623
        summary_colorlabel_menu_create(summaryview);
624
        summary_set_menu_sensitive(summaryview);
625
}
626
627
gboolean summary_show(SummaryView *summaryview, FolderItem *item,
628
                      gboolean update_cache)
629
{
630
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
631
        GtkTreeIter iter;
632
        GSList *mlist;
633
        gchar *buf;
634
        gboolean is_refresh;
635
        guint selected_msgnum = 0;
636
        guint displayed_msgnum = 0;
637
        gboolean moved;
638
        gboolean selection_done = FALSE;
639
        gboolean do_qsearch = FALSE;
640
        gboolean set_column_order_required = FALSE;
641
        const gchar *key = NULL;
642
643
        if (summary_is_locked(summaryview)) return FALSE;
644
645
        inc_lock();
646
        summary_lock(summaryview);
647
648
        STATUSBAR_POP(summaryview->mainwin);
649
650
        is_refresh = (item == summaryview->folder_item &&
651
                      update_cache == FALSE) ? TRUE : FALSE;
652
        selected_msgnum = summary_get_msgnum(summaryview,
653
                                             summaryview->selected);
654
        displayed_msgnum = summary_get_msgnum(summaryview,
655
                                              summaryview->displayed);
656
        if (summaryview->folder_item)
657
                summaryview->folder_item->last_selected = selected_msgnum;
658
        if (!is_refresh) {
659
                if (item)
660
                        selected_msgnum = item->last_selected;
661
                else
662
                        selected_msgnum = 0;
663
                displayed_msgnum = 0;
664
        }
665
666
        /* process the marks if any */
667
        if (summaryview->mainwin->lock_count == 0 &&
668
            (summaryview->moved > 0 || summaryview->copied > 0)) {
669
                AlertValue val;
670
671
                val = alertpanel(_("Process mark"),
672
                                 _("Some marks are left. Process it?"),
673
                                 GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL);
674
                if (G_ALERTDEFAULT == val) {
675
                        summary_unlock(summaryview);
676
                        summary_execute(summaryview);
677
                        summary_lock(summaryview);
678
                        GTK_EVENTS_FLUSH();
679
                } else if (G_ALERTALTERNATE == val) {
680
                        summary_write_cache(summaryview);
681
                        GTK_EVENTS_FLUSH();
682
                } else {
683
                        summary_unlock(summaryview);
684
                        inc_unlock();
685
                        return FALSE;
686
                }
687
        } else
688
                summary_write_cache(summaryview);
689
690
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item) !=
691
            FOLDER_ITEM_IS_SENT_FOLDER(item))
692
                set_column_order_required = TRUE;
693
694
        folderview_set_opened_item(summaryview->folderview, item);
695
696
        summary_clear_list_full(summaryview, is_refresh);
697
698
        buf = NULL;
699
        if (!item || !item->path || !item->parent || item->no_select ||
700
            (FOLDER_TYPE(item->folder) == F_MH && item->stype != F_VIRTUAL &&
701
             ((buf = folder_item_get_path(item)) == NULL ||
702
              change_dir(buf) < 0))) {
703
                g_free(buf);
704
                debug_print("empty folder\n\n");
705
                summary_clear_all(summaryview);
706
                summaryview->folder_item = item;
707
                if (item)
708
                        item->qsearch_cond_type = QS_ALL;
709
                if (set_column_order_required)
710
                        summary_set_column_order(summaryview);
711
                summary_unlock(summaryview);
712
                inc_unlock();
713
                return TRUE;
714
        }
715
        g_free(buf);
716
717
        if (!is_refresh)
718
                messageview_clear(summaryview->messageview);
719
720
        summaryview->folder_item = item;
721
        if (set_column_order_required)
722
                summary_set_column_order(summaryview);
723
724
        g_signal_handlers_block_matched(G_OBJECT(treeview),
725
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
726
                                        0, 0, NULL, NULL, summaryview);
727
728
        buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
729
        debug_print("%s\n", buf);
730
        STATUSBAR_PUSH(summaryview->mainwin, buf);
731
        g_free(buf);
732
733
        main_window_cursor_wait(summaryview->mainwin);
734
735
        mlist = folder_item_get_msg_list(item, !update_cache);
736
737
        statusbar_pop_all();
738
        STATUSBAR_POP(summaryview->mainwin);
739
740
        /* set tree store and hash table from the msginfo list, and
741
           create the thread */
742
        summaryview->all_mlist = mlist;
743
744
        if (prefs_common.show_searchbar &&
745
            (prefs_common.persist_qsearch_filter || is_refresh)) {
746
                if (item->qsearch_cond_type > QS_ALL)
747
                        do_qsearch = TRUE;
748
                if (is_refresh) {
749
                        key = gtk_entry_get_text
750
                                (GTK_ENTRY(summaryview->qsearch->entry));
751
                        if (key && *key != '\0')
752
                                do_qsearch = TRUE;
753
                        else
754
                                key = NULL;
755
                }
756
        }
757
758
        if (do_qsearch) {
759
                gint index;
760
                QSearchCondType type = item->qsearch_cond_type;
761
762
                index = menu_find_option_menu_index
763
                        (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
764
                         GINT_TO_POINTER(type), NULL);
765
                if (index > 0) {
766
                        gtk_option_menu_set_history
767
                                (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
768
                                 index);
769
                } else {
770
                        gtk_option_menu_set_history
771
                                (GTK_OPTION_MENU(summaryview->qsearch->optmenu),
772
                                 0);
773
                        type = QS_ALL;
774
                }
775
776
                if (type > QS_ALL || key) {
777
                        summaryview->flt_mlist =
778
                                quick_search_filter(summaryview->qsearch,
779
                                                    type, key);
780
                        summaryview->on_filter = TRUE;
781
                        summary_set_tree_model_from_list
782
                                (summaryview, summaryview->flt_mlist);
783
                        summary_update_status(summaryview);
784
                } else {
785
                        item->qsearch_cond_type = QS_ALL;
786
                        summary_set_tree_model_from_list(summaryview, mlist);
787
                }
788
        } else {
789
                item->qsearch_cond_type = QS_ALL;
790
                summary_set_tree_model_from_list(summaryview, mlist);
791
        }
792
793
        if (mlist)
794
                gtk_widget_grab_focus(GTK_WIDGET(treeview));
795
796
        summary_write_cache(summaryview);
797
798
        item->opened = TRUE;
799
800
        g_signal_handlers_unblock_matched(G_OBJECT(treeview),
801
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
802
                                          0, 0, NULL, NULL, summaryview);
803
804
        if (is_refresh) {
805
                summary_update_display_state(summaryview, displayed_msgnum,
806
                                             selected_msgnum);
807
808
                if (!summaryview->selected) {
809
                        /* no selected message - select first unread
810
                           message, but do not display it */
811
                        if (summary_find_next_flagged_msg
812
                                (summaryview, &iter, NULL, MSG_UNREAD, FALSE)) {
813
                                summary_select_row(summaryview, &iter,
814
                                                   FALSE, TRUE);
815
                        } else if ((summaryview->on_filter &&
816
                                    summaryview->flt_msg_total > 1) ||
817
                                   (!summaryview->on_filter &&
818
                                    item->total > 1)) {
819
                                g_signal_emit_by_name
820
                                        (treeview, "move-cursor",
821
                                         GTK_MOVEMENT_BUFFER_ENDS,
822
                                         item->sort_type == SORT_DESCENDING ?
823
                                         -1 : 1, &moved);
824
                                GTK_EVENTS_FLUSH();
825
                                summary_scroll_to_selected(summaryview, TRUE);
826
                        } else if (gtk_tree_model_get_iter_first
827
                                        (GTK_TREE_MODEL(summaryview->store),
828
                                         &iter)) {
829
                                summary_select_row(summaryview, &iter,
830
                                                   FALSE, TRUE);
831
                        }
832
                }
833
                selection_done = TRUE;
834
        } else if (prefs_common.remember_last_selected) {
835
                summary_unlock(summaryview);
836
                summary_select_by_msgnum(summaryview, selected_msgnum);
837
                summary_lock(summaryview);
838
839
                if (summaryview->selected)
840
                        selection_done = TRUE;
841
        }
842
843
        if (!selection_done) {
844
                /* select first unread message */
845
                if (summary_find_next_flagged_msg(summaryview, &iter, NULL,
846
                                                  MSG_UNREAD, FALSE)) {
847
                        if (prefs_common.open_unread_on_enter ||
848
                            prefs_common.always_show_msg) {
849
                                summary_unlock(summaryview);
850
                                summary_select_row(summaryview, &iter,
851
                                                   TRUE, TRUE);
852
                                summary_lock(summaryview);
853
                        } else
854
                                summary_select_row(summaryview, &iter,
855
                                                   FALSE, TRUE);
856
                } else {
857
                        summary_unlock(summaryview);
858
                        if ((summaryview->on_filter &&
859
                             summaryview->flt_msg_total > 1) ||
860
                            (!summaryview->on_filter && item->total > 1)) {
861
                                g_signal_emit_by_name
862
                                        (treeview, "move-cursor",
863
                                         GTK_MOVEMENT_BUFFER_ENDS,
864
                                         item->sort_type == SORT_DESCENDING ?
865
                                         -1 : 1, &moved);
866
                        } else if (gtk_tree_model_get_iter_first
867
                                        (GTK_TREE_MODEL(summaryview->store),
868
                                         &iter)) {
869
                                summary_select_row(summaryview, &iter,
870
                                                   FALSE, TRUE);
871
                        }
872
                        summary_lock(summaryview);
873
                        GTK_EVENTS_FLUSH();
874
                        summary_scroll_to_selected(summaryview, TRUE);
875
                }
876
        }
877
878
        summary_status_show(summaryview);
879
        summary_set_menu_sensitive(summaryview);
880
        main_window_set_toolbar_sensitive(summaryview->mainwin);
881
882
        debug_print("\n");
883
        STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
884
885
        main_window_cursor_normal(summaryview->mainwin);
886
        summary_unlock(summaryview);
887
        inc_unlock();
888
889
        return TRUE;
890
}
891
892
static void summary_unset_sort_column_id(SummaryView *summaryview)
893
{
894
        gint id;
895
        GtkSortType order;
896
897
        if (gtk_tree_sortable_get_sort_column_id
898
                (GTK_TREE_SORTABLE(summaryview->store), &id, &order) &&
899
            id >= 0 && id < N_SUMMARY_VISIBLE_COLS) {
900
                GtkTreeViewColumn *column = summaryview->columns[id];
901
                column->sort_column_id = -1;
902
                gtk_tree_view_column_set_sort_indicator(column, FALSE);
903
        }
904
905
        gtkut_tree_sortable_unset_sort_column_id
906
                (GTK_TREE_SORTABLE(summaryview->store));
907
}
908
909
void summary_clear_list(SummaryView *summaryview)
910
{
911
        summary_clear_list_full(summaryview, FALSE);
912
}
913
914
static void summary_clear_list_full(SummaryView *summaryview,
915
                                    gboolean is_refresh)
916
{
917
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
918
        GtkAdjustment *adj;
919
920
        if (summaryview->folder_item) {
921
                folder_item_close(summaryview->folder_item);
922
                summaryview->folder_item = NULL;
923
        }
924
925
        summaryview->display_msg = FALSE;
926
927
        summaryview->selected = NULL;
928
        summaryview->displayed = NULL;
929
930
        summary_selection_list_free(summaryview);
931
932
        summaryview->total_size = 0;
933
        summaryview->deleted = summaryview->moved = 0;
934
        summaryview->copied = 0;
935
936
        summary_msgid_table_destroy(summaryview);
937
938
        summaryview->tmp_mlist = NULL;
939
        summaryview->to_folder = NULL;
940
        if (summaryview->folder_table) {
941
                g_hash_table_destroy(summaryview->folder_table);
942
                summaryview->folder_table = NULL;
943
        }
944
        summaryview->filtered = 0;
945
        summaryview->flt_count = 0;
946
        summaryview->flt_total = 0;
947
948
        summaryview->on_button_press = FALSE;
949
        summaryview->can_toggle_selection = TRUE;
950
        summaryview->on_drag = FALSE;
951
        if (summaryview->pressed_path) {
952
                gtk_tree_path_free(summaryview->pressed_path);
953
                summaryview->pressed_path = NULL;
954
        }
955
        if (summaryview->drag_list) {
956
                g_free(summaryview->drag_list);
957
                summaryview->drag_list = NULL;
958
        }
959
960
        if (summaryview->flt_mlist) {
961
                g_slist_free(summaryview->flt_mlist);
962
                summaryview->flt_mlist = NULL;
963
        }
964
        summaryview->total_flt_msg_size = 0;
965
        summaryview->flt_msg_total = 0;
966
        summaryview->flt_deleted = summaryview->flt_moved = 0;
967
        summaryview->flt_copied = 0;
968
        summaryview->flt_new = summaryview->flt_unread = 0;
969
        if (!is_refresh) {
970
                quick_search_clear_entry(summaryview->qsearch);
971
                gtk_option_menu_set_history
972
                        (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0);
973
        }
974
        summaryview->on_filter = FALSE;
975
976
        procmsg_msg_list_free(summaryview->all_mlist);
977
        summaryview->all_mlist = NULL;
978
979
        gtkut_tree_view_fast_clear(treeview, summaryview->store);
980
981
        /* ensure that the "value-changed" signal is always emitted */
982
        adj = gtk_tree_view_get_vadjustment(treeview);
983
        adj->value = 0.0;
984
985
        summary_unset_sort_column_id(summaryview);
986
}
987
988
void summary_clear_all(SummaryView *summaryview)
989
{
990
        messageview_clear(summaryview->messageview);
991
        summary_clear_list(summaryview);
992
        summary_set_menu_sensitive(summaryview);
993
        main_window_set_toolbar_sensitive(summaryview->mainwin);
994
        summary_status_show(summaryview);
995
}
996
997
void summary_lock(SummaryView *summaryview)
998
{
999
        summaryview->lock_count++;
1000
}
1001
1002
void summary_unlock(SummaryView *summaryview)
1003
{
1004
        if (summaryview->lock_count)
1005
                summaryview->lock_count--;
1006
}
1007
1008
gboolean summary_is_locked(SummaryView *summaryview)
1009
{
1010
        return summaryview->lock_count > 0;
1011
}
1012
1013
SummarySelection summary_get_selection_type(SummaryView *summaryview)
1014
{
1015
        SummarySelection selection;
1016
        GList *rows;
1017
1018
        rows = summary_get_selected_rows(summaryview);
1019
1020
        if (!summaryview->folder_item || summaryview->folder_item->total == 0)
1021
                selection = SUMMARY_NONE;
1022
        else if (!rows)
1023
                selection = SUMMARY_SELECTED_NONE;
1024
        else if (rows && !rows->next)
1025
                selection = SUMMARY_SELECTED_SINGLE;
1026
        else
1027
                selection = SUMMARY_SELECTED_MULTIPLE;
1028
1029
        return selection;
1030
}
1031
1032
static GList *summary_get_selected_rows(SummaryView *summaryview)
1033
{
1034
        if (!summaryview->selection_list)
1035
                summaryview->selection_list =
1036
                        gtk_tree_selection_get_selected_rows
1037
                                (summaryview->selection, NULL);
1038
1039
        return summaryview->selection_list;
1040
}
1041
1042
static void summary_selection_list_free(SummaryView *summaryview)
1043
{
1044
        if (summaryview->selection_list) {
1045
                g_list_foreach(summaryview->selection_list,
1046
                               (GFunc)gtk_tree_path_free, NULL);
1047
                g_list_free(summaryview->selection_list);
1048
                summaryview->selection_list = NULL;
1049
        }
1050
}
1051
1052
GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1053
{
1054
        GSList *mlist = NULL;
1055
        GList *rows;
1056
        GList *cur;
1057
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1058
        GtkTreeIter iter;
1059
        MsgInfo *msginfo;
1060
1061
        rows = summary_get_selected_rows(summaryview);
1062
        for (cur = rows; cur != NULL; cur = cur->next) {
1063
                gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
1064
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1065
                mlist = g_slist_prepend(mlist, msginfo);
1066
        }
1067
1068
        mlist = g_slist_reverse(mlist);
1069
1070
        return mlist;
1071
}
1072
1073
GSList *summary_get_changed_msg_list(SummaryView *summaryview)
1074
{
1075
        MsgInfo *msginfo;
1076
        GSList *mlist = NULL;
1077
        GSList *cur;
1078
1079
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1080
                msginfo = (MsgInfo *)cur->data;
1081
                if (MSG_IS_FLAG_CHANGED(msginfo->flags))
1082
                        mlist = g_slist_prepend(mlist, msginfo);
1083
        }
1084
1085
        return g_slist_reverse(mlist);
1086
}
1087
1088
GSList *summary_get_msg_list(SummaryView *summaryview)
1089
{
1090
        if (summaryview->on_filter)
1091
                return g_slist_copy(summaryview->flt_mlist);
1092
        else
1093
                return g_slist_copy(summaryview->all_mlist);
1094
}
1095
1096
GSList *summary_get_flagged_msg_list(SummaryView *summaryview,
1097
                                     MsgPermFlags flags)
1098
{
1099
        MsgInfo *msginfo;
1100
        GSList *list, *cur;
1101
        GSList *mlist = NULL;
1102
1103
        if (summaryview->on_filter)
1104
                list = summaryview->flt_mlist;
1105
        else
1106
                list = summaryview->all_mlist;
1107
1108
        for (cur = list; cur != NULL; cur = cur->next) {
1109
                msginfo = (MsgInfo *)cur->data;
1110
                if ((msginfo->flags.perm_flags & flags) != 0)
1111
                        mlist = g_slist_prepend(mlist, msginfo);
1112
        }
1113
1114
        return g_slist_reverse(mlist);
1115
}
1116
1117
static void summary_update_msg_list(SummaryView *summaryview)
1118
{
1119
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1120
        GtkTreeIter iter;
1121
        GSList *mlist = NULL;
1122
        MsgInfo *msginfo;
1123
        gboolean valid;
1124
1125
        if (summaryview->on_filter)
1126
                return;
1127
1128
        g_slist_free(summaryview->all_mlist);
1129
        summaryview->all_mlist = NULL;
1130
1131
        valid = gtk_tree_model_get_iter_first(model, &iter);
1132
1133
        while (valid) {
1134
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1135
                mlist = g_slist_prepend(mlist, msginfo);
1136
                valid = gtkut_tree_model_next(model, &iter);
1137
        }
1138
1139
        summaryview->all_mlist = g_slist_reverse(mlist);
1140
1141
}
1142
1143
static gboolean summary_msgid_table_create_func(GtkTreeModel *model,
1144
                                                GtkTreePath *path,
1145
                                                GtkTreeIter *iter,
1146
                                                gpointer data)
1147
{
1148
        GHashTable *msgid_table = (GHashTable *)data;
1149
        MsgInfo *msginfo;
1150
        GtkTreeIter *iter_;
1151
1152
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
1153
1154
        if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1155
            !MSG_IS_DELETED(msginfo->flags) &&
1156
            msginfo->msgid && msginfo->msgid[0] != '\0') {
1157
                iter_ = gtk_tree_iter_copy(iter);
1158
                g_hash_table_replace(msgid_table, msginfo->msgid, iter_);
1159
        }
1160
1161
        return FALSE;
1162
}
1163
1164
static void summary_msgid_table_create(SummaryView *summaryview)
1165
{
1166
        GHashTable *msgid_table;
1167
1168
        g_return_if_fail(summaryview->msgid_table == NULL);
1169
1170
        msgid_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1171
                                            (GDestroyNotify)gtk_tree_iter_free);
1172
1173
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
1174
                               summary_msgid_table_create_func, msgid_table);
1175
1176
        summaryview->msgid_table = msgid_table;
1177
}
1178
1179
static void summary_msgid_table_destroy(SummaryView *summaryview)
1180
{
1181
        if (!summaryview->msgid_table)
1182
                return;
1183
1184
        g_hash_table_destroy(summaryview->msgid_table);
1185
        summaryview->msgid_table = NULL;
1186
}
1187
1188
static void summary_set_menu_sensitive(SummaryView *summaryview)
1189
{
1190
        GtkItemFactory *ifactory = summaryview->popupfactory;
1191
        SummarySelection selection;
1192
        GtkWidget *menuitem;
1193
        gboolean sens;
1194
1195
        selection = summary_get_selection_type(summaryview);
1196
        sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1197
1198
        main_window_set_menu_sensitive(summaryview->mainwin);
1199
1200
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) {
1201
                gtk_widget_show(summaryview->reedit_menuitem);
1202
                gtk_widget_show(summaryview->reedit_separator);
1203
                menu_set_sensitive(ifactory, "/Re-edit", sens);
1204
        } else {
1205
                gtk_widget_hide(summaryview->reedit_menuitem);
1206
                gtk_widget_hide(summaryview->reedit_separator);
1207
                menu_set_sensitive(ifactory, "/Re-edit", FALSE);
1208
        }
1209
1210
        if (selection == SUMMARY_NONE) {
1211
                menu_set_insensitive_all
1212
                        (GTK_MENU_SHELL(summaryview->popupmenu));
1213
                return;
1214
        }
1215
1216
        if (summaryview->folder_item &&
1217
            FOLDER_TYPE(summaryview->folder_item->folder) != F_NEWS) {
1218
                menu_set_sensitive(ifactory, "/Move...", TRUE);
1219
                menu_set_sensitive(ifactory, "/Delete", TRUE);
1220
        } else {
1221
                menu_set_sensitive(ifactory, "/Move...", FALSE);
1222
                menu_set_sensitive(ifactory, "/Delete", FALSE);
1223
        }
1224
1225
        menu_set_sensitive(ifactory, "/Copy...", TRUE);
1226
1227
        menu_set_sensitive(ifactory, "/Mark", TRUE);
1228
        menu_set_sensitive(ifactory, "/Mark/Mark",   TRUE);
1229
        menu_set_sensitive(ifactory, "/Mark/Unmark", TRUE);
1230
1231
        menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1232
        menu_set_sensitive(ifactory, "/Mark/Mark as read",   TRUE);
1233
        menu_set_sensitive(ifactory, "/Mark/Mark all read",  TRUE);
1234
1235
        if (prefs_common.enable_junk) {
1236
                gtk_widget_show(summaryview->junk_menuitem);
1237
                gtk_widget_show(summaryview->nojunk_menuitem);
1238
                gtk_widget_show(summaryview->junk_separator);
1239
                menu_set_sensitive(ifactory, "/Set as junk mail", TRUE);
1240
                menu_set_sensitive(ifactory, "/Set as not junk mail", TRUE);
1241
        } else {
1242
                gtk_widget_hide(summaryview->junk_menuitem);
1243
                gtk_widget_hide(summaryview->nojunk_menuitem);
1244
                gtk_widget_hide(summaryview->junk_separator);
1245
                menu_set_sensitive(ifactory, "/Set as junk mail", FALSE);
1246
                menu_set_sensitive(ifactory, "/Set as not junk mail", FALSE);
1247
        }
1248
1249
        menu_set_sensitive(ifactory, "/Color label", TRUE);
1250
1251
        menu_set_sensitive(ifactory, "/Reply",                          sens);
1252
        menu_set_sensitive(ifactory, "/Reply to",                  sens);
1253
        menu_set_sensitive(ifactory, "/Reply to/all",                  sens);
1254
        menu_set_sensitive(ifactory, "/Reply to/sender",          sens);
1255
        menu_set_sensitive(ifactory, "/Reply to/mailing list",          sens);
1256
        menu_set_sensitive(ifactory, "/Forward",                  TRUE);
1257
        menu_set_sensitive(ifactory, "/Forward as attachment",          TRUE);
1258
        menu_set_sensitive(ifactory, "/Redirect",                  sens);
1259
1260
        menu_set_sensitive(ifactory, "/Add sender to address book...", sens);
1261
        menu_set_sensitive(ifactory, "/Create filter rule", sens);
1262
1263
        menu_set_sensitive(ifactory, "/View", sens);
1264
        menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1265
        menu_set_sensitive(ifactory, "/View/Message source", sens);
1266
        menu_set_sensitive(ifactory, "/View/All headers", sens);
1267
1268
        menu_set_sensitive(ifactory, "/Print...",   TRUE);
1269
1270
        summary_lock(summaryview);
1271
        menuitem = gtk_item_factory_get_widget(ifactory, "/View/All headers");
1272
        gtk_check_menu_item_set_active
1273
                (GTK_CHECK_MENU_ITEM(menuitem),
1274
                 summaryview->messageview->textview->show_all_headers);
1275
        summary_unlock(summaryview);
1276
}
1277
1278
static void summary_select_prev_flagged(SummaryView *summaryview,
1279
                                        MsgPermFlags flags,
1280
                                        const gchar *title,
1281
                                        const gchar *ask_msg,
1282
                                        const gchar *notice)
1283
{
1284
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1285
        GtkTreeIter prev, iter;
1286
        gboolean start_from_prev = FALSE;
1287
        gboolean found;
1288
1289
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1290
                                               &iter))
1291
                return;
1292
1293
        if (!messageview_is_visible(summaryview->messageview) ||
1294
            summary_row_is_displayed(summaryview, &iter))
1295
                start_from_prev = TRUE;
1296
1297
        found = summary_find_prev_flagged_msg
1298
                (summaryview, &prev, &iter, flags, start_from_prev);
1299
1300
        if (!found) {
1301
                AlertValue val;
1302
1303
                val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1304
                                 NULL);
1305
                if (val != G_ALERTDEFAULT) return;
1306
                found = summary_find_prev_flagged_msg(summaryview, &prev, NULL,
1307
                                                      flags, start_from_prev);
1308
        }
1309
1310
        if (!found) {
1311
                if (notice)
1312
                        alertpanel_notice(notice);
1313
        } else
1314
                summary_select_row
1315
                        (summaryview, &prev,
1316
                         messageview_is_visible(summaryview->messageview),
1317
                         FALSE);
1318
}
1319
1320
static void summary_select_next_flagged(SummaryView *summaryview,
1321
                                        MsgPermFlags flags,
1322
                                        const gchar *title,
1323
                                        const gchar *ask_msg,
1324
                                        const gchar *notice)
1325
{
1326
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1327
        GtkTreeIter next, iter;
1328
        gboolean start_from_next = FALSE;
1329
        gboolean found;
1330
1331
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1332
                                               &iter)) {
1333
                if (!gtk_tree_model_get_iter_first(model, &iter))
1334
                        return;
1335
        }
1336
1337
        if (!messageview_is_visible(summaryview->messageview) ||
1338
            summary_row_is_displayed(summaryview, &iter))
1339
                start_from_next = TRUE;
1340
1341
        found = summary_find_next_flagged_msg
1342
                (summaryview, &next, &iter, flags, start_from_next);
1343
1344
        if (!found) {
1345
                AlertValue val;
1346
1347
                val = alertpanel(title, ask_msg, GTK_STOCK_YES, GTK_STOCK_NO,
1348
                                 NULL);
1349
                if (val != G_ALERTDEFAULT) return;
1350
                found = summary_find_next_flagged_msg(summaryview, &next, NULL,
1351
                                                      flags, start_from_next);
1352
        }
1353
1354
        if (!found) {
1355
                if (notice)
1356
                        alertpanel_notice(notice);
1357
        } else
1358
                summary_select_row
1359
                        (summaryview, &next,
1360
                         messageview_is_visible(summaryview->messageview),
1361
                         FALSE);
1362
}
1363
1364
static void summary_select_next_flagged_or_folder(SummaryView *summaryview,
1365
                                                  MsgPermFlags flags,
1366
                                                  const gchar *title,
1367
                                                  const gchar *ask_msg,
1368
                                                  const gchar *notice)
1369
{
1370
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1371
        GtkTreeIter iter, next;
1372
        gboolean start_from_next = FALSE;
1373
1374
        if (!gtkut_tree_row_reference_get_iter(model, summaryview->selected,
1375
                                               &iter)) {
1376
                if (!gtk_tree_model_get_iter_first(model, &iter))
1377
                        return;
1378
        }
1379
1380
        if (!messageview_is_visible(summaryview->messageview) ||
1381
            summary_row_is_displayed(summaryview, &iter))
1382
                start_from_next = TRUE;
1383
1384
        while (summary_find_next_flagged_msg
1385
                (summaryview, &next, &iter, flags, start_from_next) == FALSE) {
1386
                AlertValue val;
1387
1388
                val = alertpanel(title, ask_msg,
1389
                                 GTK_STOCK_YES, GTK_STOCK_NO,
1390
                                 _("_Search again"));
1391
1392
                if (val == G_ALERTDEFAULT) {
1393
                        folderview_select_next_unread(summaryview->folderview);
1394
                        return;
1395
                } else if (val == G_ALERTOTHER) {
1396
                        start_from_next = FALSE;
1397
                        if (!gtk_tree_model_get_iter_first(model, &iter))
1398
                                return;
1399
                } else
1400
                        return;
1401
        }
1402
1403
        summary_select_row(summaryview, &next,
1404
                           messageview_is_visible(summaryview->messageview),
1405
                           FALSE);
1406
}
1407
1408
void summary_select_prev_unread(SummaryView *summaryview)
1409
{
1410
        summary_select_prev_flagged(summaryview, MSG_UNREAD,
1411
                                    _("No more unread messages"),
1412
                                    _("No unread message found. "
1413
                                      "Search from the end?"),
1414
                                    _("No unread messages."));
1415
}
1416
1417
void summary_select_next_unread(SummaryView *summaryview)
1418
{
1419
        summary_select_next_flagged_or_folder(summaryview, MSG_UNREAD,
1420
                                              _("No more unread messages"),
1421
                                              _("No unread message found. "
1422
                                                "Go to next folder?"),
1423
                                              NULL);
1424
}
1425
1426
void summary_select_prev_new(SummaryView *summaryview)
1427
{
1428
        summary_select_prev_flagged(summaryview, MSG_NEW,
1429
                                    _("No more new messages"),
1430
                                    _("No new message found. "
1431
                                      "Search from the end?"),
1432
                                    _("No new messages."));
1433
}
1434
1435
void summary_select_next_new(SummaryView *summaryview)
1436
{
1437
        summary_select_next_flagged_or_folder(summaryview, MSG_NEW,
1438
                                              _("No more new messages"),
1439
                                              _("No new message found. "
1440
                                                "Go to next folder?"),
1441
                                              NULL);
1442
}
1443
1444
void summary_select_prev_marked(SummaryView *summaryview)
1445
{
1446
        summary_select_prev_flagged(summaryview, MSG_MARKED,
1447
                                    _("No more marked messages"),
1448
                                    _("No marked message found. "
1449
                                      "Search from the end?"),
1450
                                    _("No marked messages."));
1451
}
1452
1453
void summary_select_next_marked(SummaryView *summaryview)
1454
{
1455
        summary_select_next_flagged(summaryview, MSG_MARKED,
1456
                                    _("No more marked messages"),
1457
                                    _("No marked message found. "
1458
                                      "Search from the beginning?"),
1459
                                    _("No marked messages."));
1460
}
1461
1462
void summary_select_prev_labeled(SummaryView *summaryview)
1463
{
1464
        summary_select_prev_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1465
                                    _("No more labeled messages"),
1466
                                    _("No labeled message found. "
1467
                                      "Search from the end?"),
1468
                                    _("No labeled messages."));
1469
}
1470
1471
void summary_select_next_labeled(SummaryView *summaryview)
1472
{
1473
        summary_select_next_flagged(summaryview, MSG_CLABEL_FLAG_MASK,
1474
                                    _("No more labeled messages"),
1475
                                    _("No labeled message found. "
1476
                                      "Search from the beginning?"),
1477
                                    _("No labeled messages."));
1478
}
1479
1480
void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1481
{
1482
        GtkTreeIter iter;
1483
1484
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1485
                summary_select_row(summaryview, &iter, FALSE, TRUE);
1486
}
1487
1488
gboolean summary_select_by_msginfo(SummaryView *summaryview, MsgInfo *msginfo)
1489
{
1490
        GtkTreeIter iter;
1491
1492
        if (summaryview->folder_item != msginfo->folder)
1493
                return FALSE;
1494
1495
        if (summary_find_msg_by_msgnum(summaryview, msginfo->msgnum, &iter)) {
1496
                summary_select_row(summaryview, &iter,
1497
                           messageview_is_visible(summaryview->messageview),
1498
                           TRUE);
1499
                return TRUE;
1500
        }
1501
1502
        return FALSE;
1503
}
1504
1505
MsgInfo *summary_get_msginfo_by_msgnum(SummaryView *summaryview, guint msgnum)
1506
{
1507
        GtkTreeIter iter;
1508
        MsgInfo *msginfo = NULL;
1509
1510
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
1511
                GET_MSG_INFO(msginfo, &iter);
1512
1513
        return msginfo;
1514
}
1515
1516
/**
1517
 * summary_select_row:
1518
 * @summaryview: Summary view.
1519
 * @node: Summary tree node.
1520
 * @display_msg: TRUE to display the selected message.
1521
 * @do_refresh: TRUE to refresh the widget.
1522
 *
1523
 * Select @node (bringing it into view by scrolling and expanding its
1524
 * thread, if necessary) and unselect all others.  If @display_msg is
1525
 * TRUE, display the corresponding message in the message view.
1526
 * If @do_refresh is TRUE, the widget is refreshed.
1527
 **/
1528
void summary_select_row(SummaryView *summaryview, GtkTreeIter *iter,
1529
                         gboolean display_msg, gboolean do_refresh)
1530
{
1531
        GtkTreePath *path;
1532
1533
        if (!iter)
1534
                return;
1535
1536
        gtkut_tree_view_expand_parent_all
1537
                (GTK_TREE_VIEW(summaryview->treeview), iter);
1538
1539
        if (do_refresh)
1540
                gtk_widget_grab_focus(summaryview->treeview);
1541
1542
        summaryview->display_msg = display_msg;
1543
        path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
1544
                                       iter);
1545
        gtk_tree_view_set_cursor(GTK_TREE_VIEW(summaryview->treeview), path,
1546
                                 NULL, FALSE);
1547
        if (do_refresh) {
1548
                GTK_EVENTS_FLUSH();
1549
                gtk_tree_view_scroll_to_cell
1550
                        (GTK_TREE_VIEW(summaryview->treeview),
1551
                         path, NULL, TRUE, 0.5, 0.0);
1552
        } else {
1553
                gtkut_tree_view_scroll_to_cell
1554
                        (GTK_TREE_VIEW(summaryview->treeview), path,
1555
                         !summaryview->on_button_press);
1556
        }
1557
1558
        gtk_tree_path_free(path);
1559
}
1560
1561
static void summary_scroll_to_selected(SummaryView *summaryview,
1562
                                       gboolean align_center)
1563
{
1564
        GtkTreePath *path;
1565
1566
        if (!summaryview->selected)
1567
                return;
1568
1569
        path = gtk_tree_row_reference_get_path(summaryview->selected);
1570
        if (path) {
1571
                if (align_center)
1572
                        gtk_tree_view_scroll_to_cell
1573
                                (GTK_TREE_VIEW(summaryview->treeview),
1574
                                 path, NULL, TRUE, 0.5, 0.0);
1575
                else
1576
                        gtkut_tree_view_scroll_to_cell
1577
                                (GTK_TREE_VIEW(summaryview->treeview), path,
1578
                                 FALSE);
1579
                gtk_tree_path_free(path);
1580
        }
1581
}
1582
1583
static MsgInfo *summary_get_msginfo(SummaryView *summaryview,
1584
                                    GtkTreeRowReference *row)
1585
{
1586
        GtkTreeIter iter;
1587
        MsgInfo *msginfo = NULL;
1588
1589
        if (!row)
1590
                return 0;
1591
        if (!gtkut_tree_row_reference_get_iter
1592
                (GTK_TREE_MODEL(summaryview->store), row, &iter))
1593
                return 0;
1594
1595
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), &iter,
1596
                           S_COL_MSG_INFO, &msginfo, -1);
1597
1598
        return msginfo;
1599
}
1600
1601
static guint summary_get_msgnum(SummaryView *summaryview,
1602
                                GtkTreeRowReference *row)
1603
{
1604
        MsgInfo *msginfo;
1605
1606
        msginfo = summary_get_msginfo(summaryview, row);
1607
        if (!msginfo)
1608
                return 0;
1609
1610
        return msginfo->msgnum;
1611
}
1612
1613
static gboolean summary_find_prev_msg(SummaryView *summaryview,
1614
                                      GtkTreeIter *prev, GtkTreeIter *iter)
1615
{
1616
        GtkTreeIter iter_;
1617
        MsgInfo *msginfo;
1618
        gboolean valid = TRUE;
1619
1620
        if (!iter)
1621
                return FALSE;
1622
1623
        iter_ = *iter;
1624
1625
        while (valid) {
1626
                GET_MSG_INFO(msginfo, &iter_);
1627
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1628
                    !MSG_IS_DELETED(msginfo->flags)) {
1629
                        *prev = iter_;
1630
                        return TRUE;
1631
                }
1632
                valid = gtkut_tree_model_prev
1633
                        (GTK_TREE_MODEL(summaryview->store), &iter_);
1634
        }
1635
1636
        return FALSE;
1637
}
1638
1639
static gboolean summary_find_next_msg(SummaryView *summaryview,
1640
                                      GtkTreeIter *next, GtkTreeIter *iter)
1641
{
1642
        GtkTreeIter iter_;
1643
        MsgInfo *msginfo;
1644
        gboolean valid = TRUE;
1645
1646
        if (!iter)
1647
                return FALSE;
1648
1649
        iter_ = *iter;
1650
1651
        while (valid) {
1652
                GET_MSG_INFO(msginfo, &iter_);
1653
                if (msginfo && !MSG_IS_INVALID(msginfo->flags) &&
1654
                    !MSG_IS_DELETED(msginfo->flags)) {
1655
                        *next = iter_;
1656
                        return TRUE;
1657
                }
1658
                valid = gtkut_tree_model_next
1659
                        (GTK_TREE_MODEL(summaryview->store), &iter_);
1660
        }
1661
1662
        return FALSE;
1663
}
1664
1665
static gboolean summary_find_nearest_msg(SummaryView *summaryview,
1666
                                         GtkTreeIter *target, GtkTreeIter *iter)
1667
{
1668
        gboolean valid;
1669
1670
        valid = summary_find_next_msg(summaryview, target, iter);
1671
        if (!valid)
1672
                valid = summary_find_prev_msg(summaryview, target, iter);
1673
1674
        return valid;
1675
}
1676
1677
static gboolean summary_find_prev_flagged_msg(SummaryView *summaryview,
1678
                                              GtkTreeIter *prev,
1679
                                              GtkTreeIter *iter,
1680
                                              MsgPermFlags flags,
1681
                                              gboolean start_from_prev)
1682
{
1683
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1684
        GtkTreeIter iter_;
1685
        MsgInfo *msginfo;
1686
        gboolean valid = TRUE;
1687
1688
        if (iter) {
1689
                iter_ = *iter;
1690
                if (start_from_prev)
1691
                        valid = gtkut_tree_model_prev(model, &iter_);
1692
        } else
1693
                valid = gtkut_tree_model_get_iter_last(model, &iter_);
1694
1695
        for (; valid == TRUE; valid = gtkut_tree_model_prev(model, &iter_)) {
1696
                GET_MSG_INFO(msginfo, &iter_);
1697
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1698
                        *prev = iter_;
1699
                        return TRUE;
1700
                }
1701
        }
1702
1703
        return FALSE;
1704
}
1705
1706
static gboolean summary_find_next_flagged_msg(SummaryView *summaryview,
1707
                                              GtkTreeIter *next,
1708
                                              GtkTreeIter *iter,
1709
                                              MsgPermFlags flags,
1710
                                              gboolean start_from_next)
1711
{
1712
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1713
        GtkTreeIter iter_;
1714
        MsgInfo *msginfo;
1715
        gboolean valid = TRUE;
1716
1717
        if (iter) {
1718
                iter_ = *iter;
1719
                if (start_from_next)
1720
                        valid = gtkut_tree_model_next(model, &iter_);
1721
        } else
1722
                valid = gtk_tree_model_get_iter_first(model, &iter_);
1723
1724
        for (; valid == TRUE; valid = gtkut_tree_model_next(model, &iter_)) {
1725
                GET_MSG_INFO(msginfo, &iter_);
1726
                if (msginfo && (msginfo->flags.perm_flags & flags) != 0) {
1727
                        *next = iter_;
1728
                        return TRUE;
1729
                }
1730
        }
1731
1732
        return FALSE;
1733
}
1734
1735
static gboolean summary_find_msg_by_msgnum(SummaryView *summaryview,
1736
                                           guint msgnum, GtkTreeIter *found)
1737
{
1738
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1739
        GtkTreeIter iter;
1740
        MsgInfo *msginfo;
1741
        gboolean valid;
1742
1743
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
1744
             valid == TRUE; valid = gtkut_tree_model_next(model, &iter)) {
1745
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1746
                if (msginfo && msginfo->msgnum == msgnum) {
1747
                        *found = iter;
1748
                        return TRUE;
1749
                }
1750
        }
1751
1752
        return FALSE;
1753
}
1754
1755
static void summary_update_display_state(SummaryView *summaryview,
1756
                                         guint disp_msgnum, guint sel_msgnum)
1757
{
1758
        GtkTreeIter iter;
1759
1760
        if (summary_find_msg_by_msgnum(summaryview, disp_msgnum, &iter)) {
1761
                GtkTreePath *path;
1762
                GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1763
1764
                path = gtk_tree_model_get_path(model, &iter);
1765
                gtk_tree_row_reference_free(summaryview->displayed);
1766
                summaryview->displayed =
1767
                        gtk_tree_row_reference_new(model, path);
1768
                gtk_tree_path_free(path);
1769
        } else
1770
                messageview_clear(summaryview->messageview);
1771
1772
        summary_select_by_msgnum(summaryview, sel_msgnum);
1773
}
1774
1775
static guint attract_hash_func(gconstpointer key)
1776
{
1777
        gchar *str;
1778
        gchar *p;
1779
        guint h;
1780
1781
        Xstrdup_a(str, (const gchar *)key, return 0);
1782
        trim_subject_for_compare(str);
1783
1784
        p = str;
1785
        h = *p;
1786
1787
        if (h) {
1788
                for (p += 1; *p != '\0'; p++)
1789
                        h = (h << 5) - h + *p;
1790
        }
1791
1792
        return h;
1793
}
1794
1795
static gint attract_compare_func(gconstpointer a, gconstpointer b)
1796
{
1797
        return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1798
}
1799
1800
void summary_attract_by_subject(SummaryView *summaryview)
1801
{
1802
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1803
        GtkTreeIter iter;
1804
        MsgInfo *msginfo, *dest_msginfo;
1805
        GHashTable *subject_table, *order_table;
1806
        GSList *mlist = NULL, *list, *dest, *last = NULL, *next = NULL;
1807
        gboolean valid;
1808
        gint count, i;
1809
        gint *new_order;
1810
1811
        if (!summaryview->folder_item)
1812
                return;
1813
        if (summaryview->folder_item->sort_key != SORT_BY_NONE)
1814
                return;
1815
1816
        valid = gtk_tree_model_get_iter_first(model, &iter);
1817
        if (!valid)
1818
                return;
1819
1820
        debug_print("Attracting messages by subject...");
1821
        STATUSBAR_PUSH(summaryview->mainwin,
1822
                       _("Attracting messages by subject..."));
1823
1824
        main_window_cursor_wait(summaryview->mainwin);
1825
1826
        order_table = g_hash_table_new(NULL, NULL);
1827
1828
        for (count = 1; valid == TRUE; ++count) {
1829
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1830
                g_hash_table_insert(order_table, msginfo,
1831
                                    GINT_TO_POINTER(count));
1832
                mlist = g_slist_prepend(mlist, msginfo);
1833
                valid = gtk_tree_model_iter_next(model, &iter);
1834
        }
1835
        --count;
1836
1837
        mlist = g_slist_reverse(mlist);
1838
1839
        subject_table = g_hash_table_new(attract_hash_func,
1840
                                         attract_compare_func);
1841
1842
        for (list = mlist; list != NULL; list = next) {
1843
                msginfo = (MsgInfo *)list->data;
1844
1845
                next = list->next;
1846
1847
                if (!msginfo->subject) {
1848
                        last = list;
1849
                        continue;
1850
                }
1851
1852
                /* find attracting node */
1853
                dest = g_hash_table_lookup(subject_table, msginfo->subject);
1854
1855
                if (dest) {
1856
                        dest_msginfo = (MsgInfo *)dest->data;
1857
1858
                        /* if the time difference is more than 30 days,
1859
                           don't attract */
1860
                        if (ABS(msginfo->date_t - dest_msginfo->date_t)
1861
                            > 60 * 60 * 24 * 30) {
1862
                                last = list;
1863
                                continue;
1864
                        }
1865
1866
                        if (dest->next != list) {
1867
                                last->next = list->next;
1868
                                list->next = dest->next;
1869
                                dest->next = list;
1870
                        } else
1871
                                last = list;
1872
                } else
1873
                        last = list;
1874
1875
                g_hash_table_replace(subject_table, msginfo->subject, list);
1876
        }
1877
1878
        g_hash_table_destroy(subject_table);
1879
1880
        new_order = g_new(gint, count);
1881
        for (list = mlist, i = 0; list != NULL; list = list->next, ++i) {
1882
                gint old_pos;
1883
1884
                msginfo = (MsgInfo *)list->data;
1885
1886
                old_pos = GPOINTER_TO_INT
1887
                        (g_hash_table_lookup(order_table, msginfo));
1888
                new_order[i] = old_pos - 1;
1889
        }
1890
        gtk_tree_store_reorder(GTK_TREE_STORE(model), NULL, new_order);
1891
        g_free(new_order);
1892
1893
        g_slist_free(mlist);
1894
        g_hash_table_destroy(order_table);
1895
1896
        summaryview->folder_item->cache_dirty = TRUE;
1897
        summary_selection_list_free(summaryview);
1898
        summary_update_msg_list(summaryview);
1899
1900
        summary_scroll_to_selected(summaryview, TRUE);
1901
1902
        debug_print("done.\n");
1903
        STATUSBAR_POP(summaryview->mainwin);
1904
1905
        main_window_cursor_normal(summaryview->mainwin);
1906
}
1907
1908
static void summary_update_status(SummaryView *summaryview)
1909
{
1910
        GSList *cur;
1911
        MsgInfo *msginfo;
1912
        gint64 total_size = 0;
1913
        gint deleted = 0, moved = 0, copied = 0;
1914
        gint64 flt_total_size = 0;
1915
        gint flt_deleted = 0, flt_moved = 0, flt_copied = 0;
1916
        gint flt_new = 0, flt_unread = 0, flt_total = 0;
1917
1918
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
1919
                msginfo = (MsgInfo *)cur->data;
1920
1921
                if (MSG_IS_DELETED(msginfo->flags))
1922
                        deleted++;
1923
                if (MSG_IS_MOVE(msginfo->flags))
1924
                        moved++;
1925
                if (MSG_IS_COPY(msginfo->flags))
1926
                        copied++;
1927
                total_size += msginfo->size;
1928
        }
1929
1930
        for (cur = summaryview->flt_mlist; cur != NULL; cur = cur->next) {
1931
                msginfo = (MsgInfo *)cur->data;
1932
1933
                if (MSG_IS_DELETED(msginfo->flags))
1934
                        flt_deleted++;
1935
                if (MSG_IS_MOVE(msginfo->flags))
1936
                        flt_moved++;
1937
                if (MSG_IS_COPY(msginfo->flags))
1938
                        flt_copied++;
1939
                if (MSG_IS_NEW(msginfo->flags))
1940
                        flt_new++;
1941
                if (MSG_IS_UNREAD(msginfo->flags))
1942
                        flt_unread++;
1943
                flt_total++;
1944
                flt_total_size += msginfo->size;
1945
        }
1946
1947
        summaryview->total_size = total_size;
1948
        summaryview->deleted = deleted;
1949
        summaryview->moved = moved;
1950
        summaryview->copied = copied;
1951
        summaryview->total_flt_msg_size = flt_total_size;
1952
        summaryview->flt_msg_total = flt_total;
1953
        summaryview->flt_deleted = flt_deleted;
1954
        summaryview->flt_moved = flt_moved;
1955
        summaryview->flt_copied = flt_copied;
1956
        summaryview->flt_new = flt_new;
1957
        summaryview->flt_unread = flt_unread;
1958
}
1959
1960
static void summary_status_show(SummaryView *summaryview)
1961
{
1962
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
1963
        gchar *str;
1964
        gchar *del, *mv, *cp;
1965
        gchar *sel;
1966
        gchar *spc;
1967
        GList *rowlist, *cur;
1968
        guint n_selected = 0;
1969
        gint64 sel_size = 0;
1970
        MsgInfo *msginfo;
1971
        gint deleted, moved, copied;
1972
        gint new, unread, total;
1973
        gint64 total_size;
1974
1975
        if (!summaryview->folder_item) {
1976
                gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1977
                gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1978
                gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs),   "");
1979
                return;
1980
        }
1981
1982
        rowlist = summary_get_selected_rows(summaryview);
1983
        for (cur = rowlist; cur != NULL; cur = cur->next) {
1984
                GtkTreeIter iter;
1985
                GtkTreePath *path = (GtkTreePath *)cur->data;
1986
1987
                gtk_tree_model_get_iter(model, &iter, path);
1988
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
1989
                sel_size += msginfo->size;
1990
                n_selected++;
1991
        }
1992
1993
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) {
1994
                gchar *group;
1995
                group = get_abbrev_newsgroup_name
1996
                        (g_basename(summaryview->folder_item->path),
1997
                         prefs_common.ng_abbrev_len);
1998
                str = trim_string_before(group, 32);
1999
                g_free(group);
2000
        } else
2001
                str = trim_string_before(summaryview->folder_item->path, 32);
2002
        gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), str);
2003
        g_free(str);
2004
2005
        if (summaryview->on_filter) {
2006
                deleted = summaryview->flt_deleted;
2007
                moved = summaryview->flt_moved;
2008
                copied = summaryview->flt_copied;
2009
        } else {
2010
                deleted = summaryview->deleted;
2011
                moved = summaryview->moved;
2012
                copied = summaryview->copied;
2013
        }
2014
2015
        if (deleted)
2016
                del = g_strdup_printf(_("%d deleted"), deleted);
2017
        else
2018
                del = g_strdup("");
2019
        if (moved)
2020
                mv = g_strdup_printf(_("%s%d moved"),
2021
                                     deleted ? _(", ") : "", moved);
2022
        else
2023
                mv = g_strdup("");
2024
        if (copied)
2025
                cp = g_strdup_printf(_("%s%d copied"),
2026
                                     deleted || moved ? _(", ") : "", copied);
2027
        else
2028
                cp = g_strdup("");
2029
2030
        if (deleted || moved || copied)
2031
                spc = "    ";
2032
        else
2033
                spc = "";
2034
2035
        if (n_selected)
2036
                sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
2037
        else
2038
                sel = g_strdup("");
2039
        str = g_strconcat(n_selected ? itos(n_selected) : "",
2040
                          n_selected ? _(" item(s) selected") : "",
2041
                          sel, spc, del, mv, cp, NULL);
2042
        gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
2043
        g_free(str);
2044
        g_free(sel);
2045
        g_free(del);
2046
        g_free(mv);
2047
        g_free(cp);
2048
2049
        if (summaryview->on_filter) {
2050
                new = summaryview->flt_new;
2051
                unread = summaryview->flt_unread;
2052
                total = summaryview->flt_msg_total;
2053
                total_size = summaryview->total_flt_msg_size;
2054
        } else {
2055
                new = summaryview->folder_item->new;
2056
                unread = summaryview->folder_item->unread;
2057
                total = summaryview->folder_item->total;
2058
                total_size = summaryview->total_size;
2059
        }
2060
2061
        if (FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
2062
                str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2063
                                      new, unread, total,
2064
                                      to_human_readable(total_size));
2065
        } else {
2066
                str = g_strdup_printf(_("%d new, %d unread, %d total"),
2067
                                      new, unread, total);
2068
        }
2069
        gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
2070
        g_free(str);
2071
2072
        folderview_update_opened_msg_num(summaryview->folderview);
2073
}
2074
2075
void summary_sort(SummaryView *summaryview,
2076
                  FolderSortKey sort_key, FolderSortType sort_type)
2077
{
2078
        FolderItem *item = summaryview->folder_item;
2079
        GtkTreeSortable *sortable = GTK_TREE_SORTABLE(summaryview->store);
2080
        SummaryColumnType col_type, prev_col_type;
2081
        GtkTreeViewColumn *column;
2082
2083
        g_return_if_fail(sort_key >= SORT_BY_NONE && sort_key <= SORT_BY_TO);
2084
2085
        if (!item || !item->path || !item->parent || item->no_select) return;
2086
2087
        if (item->sort_key != sort_key || item->sort_type != sort_type)
2088
                item->cache_dirty = TRUE;
2089
2090
        col_type = sort_key_to_col[sort_key];
2091
        prev_col_type = sort_key_to_col[item->sort_key];
2092
2093
        if (col_type == -1) {
2094
                item->sort_key = SORT_BY_NONE;
2095
                item->sort_type = SORT_ASCENDING;
2096
                summary_unset_sort_column_id(summaryview);
2097
                summary_set_menu_sensitive(summaryview);
2098
                return;
2099
        }
2100
2101
        debug_print("Sorting summary by key: %d...\n", sort_key);
2102
        STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
2103
2104
        main_window_cursor_wait(summaryview->mainwin);
2105
2106
        item->sort_key = sort_key;
2107
        item->sort_type = sort_type;
2108
2109
        gtk_tree_sortable_set_sort_column_id(sortable, col_type,
2110
                                             (GtkSortType)sort_type);
2111
2112
        if (prev_col_type != -1 && col_type != prev_col_type &&
2113
            prev_col_type < N_SUMMARY_VISIBLE_COLS) {
2114
                column = summaryview->columns[prev_col_type];
2115
                column->sort_column_id = -1;
2116
                gtk_tree_view_column_set_sort_indicator(column, FALSE);
2117
        }
2118
        if (col_type != S_COL_MARK && col_type != S_COL_UNREAD &&
2119
            col_type != S_COL_MIME && col_type < N_SUMMARY_VISIBLE_COLS) {
2120
                column = summaryview->columns[col_type];
2121
                column->sort_column_id = col_type;
2122
                gtk_tree_view_column_set_sort_indicator(column, TRUE);
2123
                gtk_tree_view_column_set_sort_order(column,
2124
                                                    (GtkSortType)sort_type);
2125
        }
2126
2127
        summary_selection_list_free(summaryview);
2128
        if (summaryview->all_mlist)
2129
                summary_update_msg_list(summaryview);
2130
2131
        summary_set_menu_sensitive(summaryview);
2132
2133
        summary_scroll_to_selected(summaryview, TRUE);
2134
2135
        debug_print("done.\n");
2136
        STATUSBAR_POP(summaryview->mainwin);
2137
2138
        main_window_cursor_normal(summaryview->mainwin);
2139
}
2140
2141
static gboolean summary_have_unread_children(SummaryView *summaryview,
2142
                                             GtkTreeIter *iter)
2143
{
2144
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2145
        GtkTreeIter iter_;
2146
        MsgInfo *msginfo;
2147
        gboolean valid;
2148
2149
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
2150
        if (MSG_IS_UNREAD(msginfo->flags))
2151
                return TRUE;
2152
2153
        valid = gtk_tree_model_iter_children(model, &iter_, iter);
2154
2155
        while (valid) {
2156
                if (summary_have_unread_children(summaryview, &iter_))
2157
                        return TRUE;
2158
2159
                valid = gtk_tree_model_iter_next(model, &iter_);
2160
        }
2161
2162
        return FALSE;
2163
}
2164
2165
static void summary_set_row(SummaryView *summaryview, GtkTreeIter *iter,
2166
                            MsgInfo *msginfo)
2167
{
2168
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2169
        gchar date_modified[80];
2170
        const gchar *date_s;
2171
        gchar *sw_from_s = NULL;
2172
        gchar *subject_s = NULL;
2173
        gchar *to_s = NULL;
2174
        GdkPixbuf *mark_pix = NULL;
2175
        GdkPixbuf *unread_pix = NULL;
2176
        GdkPixbuf *mime_pix = NULL;
2177
        GdkColor *foreground = NULL;
2178
        PangoWeight weight = PANGO_WEIGHT_NORMAL;
2179
        MsgFlags flags;
2180
        GdkColor color;
2181
        gint color_val;
2182
2183
        if (!msginfo) {
2184
                GET_MSG_INFO(msginfo, iter);
2185
        }
2186
2187
        if (msginfo->date_t) {
2188
                procheader_date_get_localtime(date_modified,
2189
                                              sizeof(date_modified),
2190
                                              msginfo->date_t);
2191
                date_s = date_modified;
2192
        } else if (msginfo->date)
2193
                date_s = msginfo->date;
2194
        else
2195
                date_s = _("(No Date)");
2196
        if (prefs_common.swap_from && msginfo->from && msginfo->to) {
2197
                gchar *from;
2198
2199
                Xstrdup_a(from, msginfo->from, return);
2200
                extract_address(from);
2201
                if (account_address_exist(from))
2202
                        sw_from_s = g_strconcat("-->", msginfo->to, NULL);
2203
        }
2204
2205
        if (msginfo->subject) {
2206
                if (msginfo->folder && msginfo->folder->trim_summary_subject) {
2207
                        subject_s = g_strdup(msginfo->subject);
2208
                        trim_subject(subject_s);
2209
                }
2210
        }
2211
2212
        if (msginfo->to)
2213
                to_s = procheader_get_toname(msginfo->to);
2214
2215
        flags = msginfo->flags;
2216
2217
        /* set flag pixbufs */
2218
        if (MSG_IS_DELETED(flags)) {
2219
                mark_pix = deleted_pixbuf;
2220
                foreground = &summaryview->color_dim;
2221
        } else if (MSG_IS_MOVE(flags)) {
2222
                /* mark_pix = move_pixbuf; */
2223
                foreground = &summaryview->color_marked;
2224
        } else if (MSG_IS_COPY(flags)) {
2225
                /* mark_pix = copy_pixbuf; */
2226
                foreground = &summaryview->color_marked;
2227
        } else if (MSG_IS_MARKED(flags))
2228
                mark_pix = mark_pixbuf;
2229
2230
        if (MSG_IS_NEW(flags))
2231
                unread_pix = new_pixbuf;
2232
        else if (MSG_IS_UNREAD(flags))
2233
                unread_pix = unread_pixbuf;
2234
        else if (MSG_IS_REPLIED(flags))
2235
                unread_pix = replied_pixbuf;
2236
        else if (MSG_IS_FORWARDED(flags))
2237
                unread_pix = forwarded_pixbuf;
2238
2239
        if (MSG_IS_MIME(flags))
2240
                mime_pix = clip_pixbuf;
2241
2242
        if (prefs_common.bold_unread) {
2243
                if (MSG_IS_UNREAD(flags))
2244
                        weight = PANGO_WEIGHT_BOLD;
2245
                else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store),
2246
                                                       iter)) {
2247
                        GtkTreePath *path;
2248
2249
                        path = gtk_tree_model_get_path
2250
                                (GTK_TREE_MODEL(store), iter);
2251
                        if (!gtk_tree_view_row_expanded
2252
                                (GTK_TREE_VIEW(summaryview->treeview), path) &&
2253
                            summary_have_unread_children(summaryview, iter))
2254
                                weight = PANGO_WEIGHT_BOLD;
2255
                        gtk_tree_path_free(path);
2256
                }
2257
        }
2258
2259
        color_val = MSG_GET_COLORLABEL_VALUE(flags);
2260
        if (color_val != 0) {
2261
                color = colorlabel_get_color(color_val - 1);
2262
                foreground = &color;
2263
        }
2264
2265
        gtk_tree_store_set(store, iter,
2266
                           S_COL_MARK, mark_pix,
2267
                           S_COL_UNREAD, unread_pix,
2268
                           S_COL_MIME, mime_pix,
2269
                           S_COL_SUBJECT, subject_s ? subject_s :
2270
                                             msginfo->subject ? msginfo->subject :
2271
                                          _("(No Subject)"),
2272
                           S_COL_FROM, sw_from_s ? sw_from_s :
2273
                                          msginfo->fromname ? msginfo->fromname :
2274
                                       _("(No From)"),
2275
                           S_COL_DATE, date_s,
2276
                           S_COL_SIZE, to_human_readable(msginfo->size),
2277
                           S_COL_NUMBER, msginfo->msgnum,
2278
                           S_COL_TO, to_s ? to_s : "",
2279
2280
                           S_COL_MSG_INFO, msginfo,
2281
2282
                           S_COL_LABEL, color_val,
2283
2284
                           S_COL_FOREGROUND, foreground,
2285
                           S_COL_BOLD, weight,
2286
                           -1);
2287
2288
        if (to_s)
2289
                g_free(to_s);
2290
        if (subject_s)
2291
                g_free(subject_s);
2292
        if (sw_from_s)
2293
                g_free(sw_from_s);
2294
}
2295
2296
static void summary_insert_gnode(SummaryView *summaryview, GtkTreeStore *store,
2297
                                 GtkTreeIter *iter, GtkTreeIter *parent,
2298
                                 GtkTreeIter *sibling, GNode *gnode)
2299
{
2300
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2301
2302
        if (parent && !sibling)
2303
                gtk_tree_store_append(store, iter, parent);
2304
        else
2305
                gtk_tree_store_insert_after(store, iter, parent, sibling);
2306
2307
        summary_set_row(summaryview, iter, msginfo);
2308
2309
        if (!parent) {
2310
                guint tdate;
2311
2312
                tdate = procmsg_get_thread_date(gnode);
2313
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2314
        }
2315
2316
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2317
                GtkTreeIter child;
2318
2319
                summary_insert_gnode(summaryview, store, &child, iter, NULL,
2320
                                     gnode);
2321
        }
2322
}
2323
2324
static void summary_insert_gnode_before(SummaryView *summaryview,
2325
                                        GtkTreeStore *store,
2326
                                        GtkTreeIter *iter, GtkTreeIter *parent,
2327
                                        GtkTreeIter *sibling, GNode *gnode)
2328
{
2329
        MsgInfo *msginfo = (MsgInfo *)gnode->data;
2330
2331
        gtk_tree_store_insert_before(store, iter, parent, sibling);
2332
2333
        summary_set_row(summaryview, iter, msginfo);
2334
2335
        if (!parent) {
2336
                guint tdate;
2337
2338
                tdate = procmsg_get_thread_date(gnode);
2339
                gtk_tree_store_set(store, iter, S_COL_TDATE, tdate, -1);
2340
        }
2341
2342
        for (gnode = gnode->children; gnode != NULL; gnode = gnode->next) {
2343
                GtkTreeIter child;
2344
2345
                summary_insert_gnode_before(summaryview, store, &child, iter,
2346
                                            NULL, gnode);
2347
        }
2348
}
2349
2350
static void summary_set_tree_model_from_list(SummaryView *summaryview,
2351
                                             GSList *mlist)
2352
{
2353
        GtkTreeStore *store = GTK_TREE_STORE(summaryview->store);
2354
        GtkTreeIter iter;
2355
        MsgInfo *msginfo;
2356
        GSList *cur;
2357
2358
        debug_print(_("\tSetting summary from message data..."));
2359
        STATUSBAR_PUSH(summaryview->mainwin,
2360
                       _("Setting summary from message data..."));
2361
        gdk_flush();
2362
2363
        /* temporarily remove the model for speed up */
2364
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview), NULL);
2365
2366
        if (summaryview->folder_item->threaded) {
2367
                GNode *root, *gnode;
2368
2369
                root = procmsg_get_thread_tree(mlist);
2370
2371
                for (gnode = root->children; gnode != NULL;
2372
                     gnode = gnode->next) {
2373
                        summary_insert_gnode
2374
                                (summaryview, store, &iter, NULL, NULL, gnode);
2375
                        if (gnode->children && !prefs_common.expand_thread &&
2376
                            prefs_common.bold_unread &&
2377
                            summary_have_unread_children(summaryview, &iter)) {
2378
                                gtk_tree_store_set(store, &iter,
2379
                                                   S_COL_BOLD,
2380
                                                   PANGO_WEIGHT_BOLD, -1);
2381
                        }
2382
                }
2383
2384
                g_node_destroy(root);
2385
2386
                for (cur = mlist; cur != NULL; cur = cur->next) {
2387
                        msginfo = (MsgInfo *)cur->data;
2388
2389
                        if (MSG_IS_DELETED(msginfo->flags))
2390
                                summaryview->deleted++;
2391
                        summaryview->total_size += msginfo->size;
2392
                }
2393
        } else {
2394
                GSList *rev_mlist;
2395
                GtkTreeIter iter;
2396
2397
                rev_mlist = g_slist_reverse(g_slist_copy(mlist));
2398
                for (cur = rev_mlist; cur != NULL; cur = cur->next) {
2399
                        msginfo = (MsgInfo *)cur->data;
2400
2401
                        gtk_tree_store_prepend(store, &iter, NULL);
2402
                        summary_set_row(summaryview, &iter, msginfo);
2403
2404
                        if (MSG_IS_DELETED(msginfo->flags))
2405
                                summaryview->deleted++;
2406
                        summaryview->total_size += msginfo->size;
2407
                }
2408
                g_slist_free(rev_mlist);
2409
        }
2410
2411
        gtk_tree_view_set_model(GTK_TREE_VIEW(summaryview->treeview),
2412
                                GTK_TREE_MODEL(store));
2413
2414
        if (summaryview->folder_item->threaded && prefs_common.expand_thread)
2415
                gtk_tree_view_expand_all
2416
                        (GTK_TREE_VIEW(summaryview->treeview));
2417
2418
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
2419
                summary_sort(summaryview, summaryview->folder_item->sort_key,
2420
                             summaryview->folder_item->sort_type);
2421
        }
2422
2423
        debug_print(_("done.\n"));
2424
        STATUSBAR_POP(summaryview->mainwin);
2425
}
2426
2427
struct wcachefp
2428
{
2429
        FILE *cache_fp;
2430
        FILE *mark_fp;
2431
};
2432
2433
gint summary_write_cache(SummaryView *summaryview)
2434
{
2435
        struct wcachefp fps;
2436
        FolderItem *item;
2437
        gchar *buf;
2438
        GSList *cur;
2439
2440
        item = summaryview->folder_item;
2441
        if (!item || !item->path)
2442
                return -1;
2443
        if (item->mark_queue)
2444
                item->mark_dirty = TRUE;
2445
        if (!item->cache_dirty && !item->mark_dirty)
2446
                return 0;
2447
2448
        if (item->cache_dirty) {
2449
                fps.cache_fp = procmsg_open_cache_file(item, DATA_WRITE);
2450
                if (fps.cache_fp == NULL)
2451
                        return -1;
2452
                item->mark_dirty = TRUE;
2453
        } else
2454
                fps.cache_fp = NULL;
2455
2456
        if (item->mark_dirty && item->stype != F_VIRTUAL) {
2457
                fps.mark_fp = procmsg_open_mark_file(item, DATA_WRITE);
2458
                if (fps.mark_fp == NULL) {
2459
                        if (fps.cache_fp)
2460
                                fclose(fps.cache_fp);
2461
                        return -1;
2462
                }
2463
        } else
2464
                fps.mark_fp = NULL;
2465
2466
        if (item->cache_dirty) {
2467
                buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2468
                                      item->path);
2469
                debug_print(buf);
2470
                STATUSBAR_PUSH(summaryview->mainwin, buf);
2471
                gdk_flush();
2472
                g_free(buf);
2473
        }
2474
2475
        for (cur = summaryview->all_mlist; cur != NULL; cur = cur->next) {
2476
                MsgInfo *msginfo = (MsgInfo *)cur->data;
2477
2478
                if (msginfo->folder->mark_queue != NULL) {
2479
                        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW);
2480
                }
2481
                if (fps.cache_fp)
2482
                        procmsg_write_cache(msginfo, fps.cache_fp);
2483
                if (fps.mark_fp)
2484
                        procmsg_write_flags(msginfo, fps.mark_fp);
2485
        }
2486
2487
        if (item->mark_queue)
2488
                procmsg_flush_mark_queue(item, fps.mark_fp);
2489
2490
        item->unmarked_num = 0;
2491
2492
        if (fps.cache_fp)
2493
                fclose(fps.cache_fp);
2494
        if (fps.mark_fp)
2495
                fclose(fps.mark_fp);
2496
2497
        if (item->stype == F_VIRTUAL) {
2498
                GSList *mlist;
2499
2500
                mlist = summary_get_changed_msg_list(summaryview);
2501
                if (mlist) {
2502
                        procmsg_write_flags_for_multiple_folders(mlist);
2503
                        g_slist_free(mlist);
2504
                        folderview_update_all_updated(FALSE);
2505
                }
2506
        }
2507
2508
        debug_print(_("done.\n"));
2509
2510
        if (item->cache_dirty) {
2511
                STATUSBAR_POP(summaryview->mainwin);
2512
        }
2513
2514
        item->cache_dirty = item->mark_dirty = FALSE;
2515
2516
        return 0;
2517
}
2518
2519
static gboolean summary_row_is_displayed(SummaryView *summaryview,
2520
                                         GtkTreeIter *iter)
2521
{
2522
        GtkTreePath *disp_path, *path;
2523
        gint ret;
2524
2525
        if (!summaryview->displayed || !iter)
2526
                return FALSE;
2527
2528
        disp_path = gtk_tree_row_reference_get_path(summaryview->displayed);
2529
        if (!disp_path)
2530
                return FALSE;
2531
2532
        path = gtk_tree_model_get_path(GTK_TREE_MODEL(summaryview->store),
2533
                                       iter);
2534
        if (!path) {
2535
                gtk_tree_path_free(disp_path);
2536
                return FALSE;
2537
        }
2538
2539
        ret = gtk_tree_path_compare(disp_path, path);
2540
        gtk_tree_path_free(path);
2541
        gtk_tree_path_free(disp_path);
2542
2543
        return (ret == 0);
2544
}
2545
2546
static void summary_display_msg(SummaryView *summaryview, GtkTreeIter *iter)
2547
{
2548
        summary_display_msg_full(summaryview, iter, FALSE, FALSE, FALSE);
2549
}
2550
2551
static void summary_display_msg_full(SummaryView *summaryview,
2552
                                     GtkTreeIter *iter,
2553
                                     gboolean new_window, gboolean all_headers,
2554
                                     gboolean redisplay)
2555
{
2556
        GtkTreePath *path;
2557
        MsgInfo *msginfo = NULL;
2558
        gint val;
2559
2560
        g_return_if_fail(iter != NULL);
2561
2562
        if (!new_window && !redisplay &&
2563
            summary_row_is_displayed(summaryview, iter))
2564
                return;
2565
2566
        if (summary_is_locked(summaryview)) return;
2567
        summary_lock(summaryview);
2568
2569
        STATUSBAR_POP(summaryview->mainwin);
2570
2571
        gtk_tree_model_get(GTK_TREE_MODEL(summaryview->store), iter,
2572
                           S_COL_MSG_INFO, &msginfo, -1);
2573
2574
        if (new_window) {
2575
                MessageView *msgview;
2576
2577
                msgview = messageview_create_with_new_window();
2578
                val = messageview_show(msgview, msginfo, all_headers);
2579
        } else {
2580
                MessageView *msgview = summaryview->messageview;
2581
2582
                if (!messageview_is_visible(msgview)) {
2583
                        main_window_toggle_message_view(summaryview->mainwin);
2584
                        GTK_EVENTS_FLUSH();
2585
                }
2586
                val = messageview_show(msgview, msginfo, all_headers);
2587
                if (msgview->type == MVIEW_TEXT ||
2588
                    (msgview->type == MVIEW_MIME &&
2589
                     (msgview->mimeview->opened == NULL ||
2590
                      gtk_notebook_get_current_page
2591
                        (GTK_NOTEBOOK(msgview->notebook)) == 0)))
2592
                        gtk_widget_grab_focus(summaryview->treeview);
2593
        }
2594
2595
        if (val == 0 &&
2596
            (new_window || !prefs_common.mark_as_read_on_new_window)) {
2597
                if (MSG_IS_NEW(msginfo->flags)) {
2598
                        if (summaryview->folder_item->new > 0)
2599
                                summaryview->folder_item->new--;
2600
                        if (summaryview->on_filter && summaryview->flt_new > 0)
2601
                                summaryview->flt_new--;
2602
                        inc_block_notify(TRUE);
2603
                }
2604
                if (MSG_IS_UNREAD(msginfo->flags)) {
2605
                        if (summaryview->folder_item->unread > 0)
2606
                                summaryview->folder_item->unread--;
2607
                        if (summaryview->on_filter &&
2608
                            summaryview->flt_unread > 0)
2609
                                summaryview->flt_unread--;
2610
                }
2611
2612
                if (summaryview->folder_item->stype == F_VIRTUAL) {
2613
                        if (MSG_IS_NEW(msginfo->flags) &&
2614
                            msginfo->folder->new > 0)
2615
                                msginfo->folder->new--;
2616
                        if (MSG_IS_UNREAD(msginfo->flags) &&
2617
                            msginfo->folder->unread > 0)
2618
                                msginfo->folder->unread--;
2619
                        folderview_update_item(msginfo->folder, FALSE);
2620
                }
2621
2622
                if (MSG_IS_NEW(msginfo->flags) ||
2623
                    MSG_IS_UNREAD(msginfo->flags)) {
2624
                        MSG_UNSET_PERM_FLAGS
2625
                                (msginfo->flags, MSG_NEW | MSG_UNREAD);
2626
                        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
2627
                        summaryview->folder_item->mark_dirty = TRUE;
2628
                        if (MSG_IS_IMAP(msginfo->flags))
2629
                                imap_msg_unset_perm_flags
2630
                                        (msginfo, MSG_NEW | MSG_UNREAD);
2631
                        summary_set_row(summaryview, iter, msginfo);
2632
                        summary_status_show(summaryview);
2633
                }
2634
        }
2635
2636
        path = gtk_tree_model_get_path
2637
                (GTK_TREE_MODEL(summaryview->store), iter);
2638
        if (!new_window) {
2639
                gtk_tree_row_reference_free(summaryview->displayed);
2640
                summaryview->displayed =
2641
                        gtk_tree_row_reference_new
2642
                                (GTK_TREE_MODEL(summaryview->store), path);
2643
        }
2644
        gtkut_tree_view_scroll_to_cell
2645
                (GTK_TREE_VIEW(summaryview->treeview), path,
2646
                 !summaryview->on_button_press);
2647
        gtk_tree_path_free(path);
2648
2649
        if (summaryview->folder_item->sort_key == SORT_BY_UNREAD)
2650
                summary_selection_list_free(summaryview);
2651
2652
        summary_set_menu_sensitive(summaryview);
2653
        main_window_set_toolbar_sensitive(summaryview->mainwin);
2654
2655
        trayicon_set_tooltip(NULL);
2656
        trayicon_set_notify(FALSE);
2657
2658
        statusbar_pop_all();
2659
2660
        summary_unlock(summaryview);
2661
}
2662
2663
void summary_display_msg_selected(SummaryView *summaryview,
2664
                                  gboolean new_window, gboolean all_headers)
2665
{
2666
        GtkTreeIter iter;
2667
2668
        if (summary_is_locked(summaryview)) return;
2669
2670
        if (summaryview->selected) {
2671
                if (gtkut_tree_row_reference_get_iter
2672
                        (GTK_TREE_MODEL(summaryview->store),
2673
                         summaryview->selected, &iter)) {
2674
                        summary_display_msg_full(summaryview, &iter,
2675
                                                 new_window, all_headers, TRUE);
2676
                }
2677
        }
2678
}
2679
2680
void summary_redisplay_msg(SummaryView *summaryview)
2681
{
2682
        GtkTreeIter iter;
2683
2684
        if (summaryview->displayed) {
2685
                if (gtkut_tree_row_reference_get_iter
2686
                        (GTK_TREE_MODEL(summaryview->store),
2687
                         summaryview->displayed, &iter)) {
2688
                        summary_display_msg_full(summaryview, &iter,
2689
                                                 FALSE, FALSE, TRUE);
2690
                }
2691
        }
2692
}
2693
2694
void summary_open_msg(SummaryView *summaryview)
2695
{
2696
        summary_display_msg_selected(summaryview, TRUE, FALSE);
2697
}
2698
2699
static void summary_activate_selected(SummaryView *summaryview)
2700
{
2701
        if (!summaryview->folder_item)
2702
                return;
2703
2704
        if (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item))
2705
                summary_reedit(summaryview);
2706
        else
2707
                summary_open_msg(summaryview);
2708
2709
        summaryview->display_msg = FALSE;
2710
}
2711
2712
void summary_view_source(SummaryView *summaryview)
2713
{
2714
        GtkTreeIter iter;
2715
        MsgInfo *msginfo;
2716
        SourceWindow *srcwin;
2717
2718
        if (summaryview->selected) {
2719
                if (gtkut_tree_row_reference_get_iter
2720
                        (GTK_TREE_MODEL(summaryview->store),
2721
                         summaryview->selected, &iter)) {
2722
                        GET_MSG_INFO(msginfo, &iter);
2723
2724
                        srcwin = source_window_create();
2725
                        source_window_show_msg(srcwin, msginfo);
2726
                        source_window_show(srcwin);
2727
                }
2728
        }
2729
}
2730
2731
void summary_reedit(SummaryView *summaryview)
2732
{
2733
        GtkTreeIter iter;
2734
        MsgInfo *msginfo;
2735
2736
        if (!summaryview->selected) return;
2737
        if (!FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item)) return;
2738
2739
        if (gtkut_tree_row_reference_get_iter
2740
                (GTK_TREE_MODEL(summaryview->store),
2741
                 summaryview->selected, &iter)) {
2742
                GET_MSG_INFO(msginfo, &iter);
2743
                compose_reedit(msginfo);
2744
        }
2745
}
2746
2747
gboolean summary_step(SummaryView *summaryview, GtkScrollType type)
2748
{
2749
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2750
        GtkTreeIter iter;
2751
2752
        if (summary_is_locked(summaryview)) return FALSE;
2753
2754
        if (!gtkut_tree_row_reference_get_iter
2755
                (model, summaryview->selected, &iter))
2756
                return FALSE;
2757
2758
        if (type == GTK_SCROLL_STEP_FORWARD) {
2759
                if (!gtkut_tree_model_next(model, &iter))
2760
                        return FALSE;
2761
        } else {
2762
                if (!gtkut_tree_model_prev(model, &iter))
2763
                        return FALSE;
2764
        }
2765
2766
        summary_select_row(summaryview, &iter,
2767
                           messageview_is_visible(summaryview->messageview),
2768
                           FALSE);
2769
2770
        return TRUE;
2771
}
2772
2773
void summary_toggle_view(SummaryView *summaryview)
2774
{
2775
        if (!messageview_is_visible(summaryview->messageview) &&
2776
            summaryview->selected)
2777
                summary_display_msg_selected(summaryview, FALSE, FALSE);
2778
        else
2779
                main_window_toggle_message_view(summaryview->mainwin);
2780
}
2781
2782
void summary_update_selected_rows(SummaryView *summaryview)
2783
{
2784
        GList *rows, *cur;
2785
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2786
        GtkTreeIter iter;
2787
        GtkTreePath *path;
2788
2789
        rows = summary_get_selected_rows(summaryview);
2790
        for (cur = rows; cur != NULL; cur = cur->next) {
2791
                path = (GtkTreePath *)cur->data;
2792
                gtk_tree_model_get_iter(model, &iter, path);
2793
                summary_set_row(summaryview, &iter, NULL);
2794
        }
2795
}
2796
2797
void summary_update_by_msgnum(SummaryView *summaryview, guint msgnum)
2798
{
2799
        GtkTreeIter iter;
2800
2801
        if (summary_find_msg_by_msgnum(summaryview, msgnum, &iter))
2802
                summary_set_row(summaryview, &iter, NULL);
2803
}
2804
2805
static void summary_mark_row(SummaryView *summaryview, GtkTreeIter *iter)
2806
{
2807
        MsgInfo *msginfo = NULL;
2808
2809
        GET_MSG_INFO(msginfo, iter);
2810
2811
        msginfo->to_folder = NULL;
2812
        if (MSG_IS_DELETED(msginfo->flags)) {
2813
                summaryview->deleted--;
2814
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2815
        }
2816
        if (MSG_IS_MOVE(msginfo->flags))
2817
                summaryview->moved--;
2818
        if (MSG_IS_COPY(msginfo->flags))
2819
                summaryview->copied--;
2820
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2821
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2822
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
2823
        summaryview->folder_item->mark_dirty = TRUE;
2824
        summary_set_row(summaryview, iter, msginfo);
2825
2826
        debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2827
}
2828
2829
void summary_mark(SummaryView *summaryview)
2830
{
2831
        GList *rows, *cur;
2832
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2833
        GtkTreeIter iter;
2834
        FolderSortKey sort_key = SORT_BY_NONE;
2835
        FolderSortType sort_type = SORT_ASCENDING;
2836
2837
        SORT_BLOCK(SORT_BY_MARK);
2838
2839
        rows = summary_get_selected_rows(summaryview);
2840
        for (cur = rows; cur != NULL; cur = cur->next) {
2841
                GtkTreePath *path = (GtkTreePath *)cur->data;
2842
                gtk_tree_model_get_iter(model, &iter, path);
2843
                summary_mark_row(summaryview, &iter);
2844
        }
2845
2846
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2847
                GSList *msglist;
2848
2849
                msglist = summary_get_selected_msg_list(summaryview);
2850
                imap_msg_list_set_perm_flags(msglist, MSG_MARKED);
2851
                g_slist_free(msglist);
2852
        }
2853
2854
        SORT_UNBLOCK(SORT_BY_MARK);
2855
2856
        summary_status_show(summaryview);
2857
}
2858
2859
static void summary_mark_row_as_read(SummaryView *summaryview,
2860
                                     GtkTreeIter *iter)
2861
{
2862
        MsgInfo *msginfo = NULL;
2863
2864
        GET_MSG_INFO(msginfo, iter);
2865
2866
        if (MSG_IS_NEW(msginfo->flags)) {
2867
                if (summaryview->folder_item->new > 0)
2868
                        summaryview->folder_item->new--;
2869
                if (summaryview->on_filter && summaryview->flt_new > 0)
2870
                        summaryview->flt_new--;
2871
                inc_block_notify(TRUE);
2872
        }
2873
        if (MSG_IS_UNREAD(msginfo->flags)) {
2874
                if (summaryview->folder_item->unread > 0)
2875
                        summaryview->folder_item->unread--;
2876
                if (summaryview->on_filter && summaryview->flt_unread > 0)
2877
                        summaryview->flt_unread--;
2878
        }
2879
2880
        if (summaryview->folder_item->stype == F_VIRTUAL) {
2881
                if (MSG_IS_NEW(msginfo->flags) && msginfo->folder->new > 0)
2882
                        msginfo->folder->new--;
2883
                if (MSG_IS_UNREAD(msginfo->flags) &&
2884
                    msginfo->folder->unread > 0)
2885
                        msginfo->folder->unread--;
2886
                folderview_update_item(msginfo->folder, FALSE);
2887
        }
2888
2889
        if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2890
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2891
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
2892
                summaryview->folder_item->mark_dirty = TRUE;
2893
                summary_set_row(summaryview, iter, msginfo);
2894
                debug_print(_("Message %d is marked as being read\n"),
2895
                            msginfo->msgnum);
2896
        }
2897
}
2898
2899
void summary_mark_as_read(SummaryView *summaryview)
2900
{
2901
        GList *rows, *cur;
2902
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2903
        GtkTreeIter iter;
2904
        FolderSortKey sort_key = SORT_BY_NONE;
2905
        FolderSortType sort_type = SORT_ASCENDING;
2906
2907
        SORT_BLOCK(SORT_BY_UNREAD);
2908
2909
        rows = summary_get_selected_rows(summaryview);
2910
2911
        for (cur = rows; cur != NULL; cur = cur->next) {
2912
                GtkTreePath *path = (GtkTreePath *)cur->data;
2913
2914
                gtk_tree_model_get_iter(model, &iter, path);
2915
                summary_mark_row_as_read(summaryview, &iter);
2916
        }
2917
2918
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
2919
                GSList *msglist;
2920
2921
                msglist = summary_get_selected_msg_list(summaryview);
2922
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
2923
                g_slist_free(msglist);
2924
        }
2925
2926
        SORT_UNBLOCK(SORT_BY_UNREAD);
2927
2928
        trayicon_set_tooltip(NULL);
2929
        trayicon_set_notify(FALSE);
2930
        summary_status_show(summaryview);
2931
}
2932
2933
static gboolean prepend_thread_rows_func(GtkTreeModel *model, GtkTreePath *path,
2934
                                         GtkTreeIter *iter, gpointer data)
2935
{
2936
        GSList **thr_rows = (GSList **)data;
2937
        GtkTreePath *top_path;
2938
2939
        top_path = gtk_tree_path_copy(path);
2940
        *thr_rows = g_slist_prepend(*thr_rows, top_path);
2941
        return FALSE;
2942
}
2943
2944
void summary_mark_thread_as_read(SummaryView *summaryview)
2945
{
2946
        GList *rows, *cur;
2947
        GSList *thr_rows = NULL, *top_rows = NULL, *s_cur;
2948
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
2949
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
2950
        GtkTreeIter iter, parent;
2951
        GtkTreePath *path, *top_path;
2952
        GHashTable *row_table;
2953
        MsgInfo *msginfo;
2954
        GSList *msglist = NULL;
2955
        FolderSortKey sort_key = SORT_BY_NONE;
2956
        FolderSortType sort_type = SORT_ASCENDING;
2957
2958
        SORT_BLOCK(SORT_BY_UNREAD);
2959
2960
        rows = summary_get_selected_rows(summaryview);
2961
2962
        row_table = g_hash_table_new(NULL, NULL);
2963
2964
        for (cur = rows; cur != NULL; cur = cur->next) {
2965
                path = (GtkTreePath *)cur->data;
2966
                gtk_tree_model_get_iter(model, &iter, path);
2967
                while (gtk_tree_model_iter_parent(model, &parent, &iter))
2968
                        iter = parent;
2969
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
2970
                if (!g_hash_table_lookup(row_table, msginfo)) {
2971
                        g_hash_table_insert(row_table, msginfo,
2972
                                            GINT_TO_POINTER(1));
2973
                        top_path = gtk_tree_model_get_path(model, &iter);
2974
                        top_rows = g_slist_prepend(top_rows, top_path);
2975
                        gtkut_tree_model_foreach(model, &iter,
2976
                                                 prepend_thread_rows_func,
2977
                                                 &thr_rows);
2978
                }
2979
        }
2980
        top_rows = g_slist_reverse(top_rows);
2981
        thr_rows = g_slist_reverse(thr_rows);
2982
2983
        g_hash_table_destroy(row_table);
2984
2985
        for (s_cur = thr_rows; s_cur != NULL; s_cur = s_cur->next) {
2986
                path = (GtkTreePath *)s_cur->data;
2987
                gtk_tree_model_get_iter(model, &iter, path);
2988
                summary_mark_row_as_read(summaryview, &iter);
2989
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
2990
                msglist = g_slist_prepend(msglist, msginfo);
2991
        }
2992
2993
        if (prefs_common.bold_unread) {
2994
                for (s_cur = top_rows; s_cur != NULL; s_cur = s_cur->next) {
2995
                        path = (GtkTreePath *)s_cur->data;
2996
                        gtk_tree_model_get_iter(model, &iter, path);
2997
                        if (gtk_tree_model_iter_has_child(model, &iter) &&
2998
                            !gtk_tree_view_row_expanded(treeview, path) &&
2999
                            !summary_have_unread_children(summaryview, &iter)) {
3000
                                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
3001
                                                   S_COL_BOLD,
3002
                                                   PANGO_WEIGHT_NORMAL, -1);
3003
                        }
3004
                }
3005
        }
3006
        msglist = g_slist_reverse(msglist);
3007
3008
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3009
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3010
        }
3011
3012
        g_slist_free(msglist);
3013
        g_slist_foreach(top_rows, (GFunc)gtk_tree_path_free, NULL);
3014
        g_slist_free(top_rows);
3015
        g_slist_foreach(thr_rows, (GFunc)gtk_tree_path_free, NULL);
3016
        g_slist_free(thr_rows);
3017
3018
        SORT_UNBLOCK(SORT_BY_UNREAD);
3019
3020
        trayicon_set_tooltip(NULL);
3021
        trayicon_set_notify(FALSE);
3022
        summary_status_show(summaryview);
3023
}
3024
3025
void summary_mark_all_read(SummaryView *summaryview)
3026
{
3027
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3028
        GtkTreeIter iter;
3029
        gboolean valid;
3030
        FolderSortKey sort_key = SORT_BY_NONE;
3031
        FolderSortType sort_type = SORT_ASCENDING;
3032
3033
        SORT_BLOCK(SORT_BY_UNREAD);
3034
3035
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3036
                GSList *msglist;
3037
                msglist = summary_get_flagged_msg_list(summaryview,
3038
                                                       MSG_NEW | MSG_UNREAD);
3039
                imap_msg_list_unset_perm_flags(msglist, MSG_NEW | MSG_UNREAD);
3040
                g_slist_free(msglist);
3041
        }
3042
3043
        valid = gtk_tree_model_get_iter_first(model, &iter);
3044
        while (valid) {
3045
                summary_mark_row_as_read(summaryview, &iter);
3046
                if (prefs_common.bold_unread) {
3047
                        if (gtk_tree_model_iter_has_child(model, &iter)) {
3048
                                GtkTreePath *path;
3049
3050
                                path = gtk_tree_model_get_path(model, &iter);
3051
                                if (!gtk_tree_view_row_expanded
3052
                                        (GTK_TREE_VIEW(summaryview->treeview),
3053
                                         path))
3054
                                        gtk_tree_store_set
3055
                                                (GTK_TREE_STORE(model), &iter,
3056
                                                 S_COL_BOLD,
3057
                                                 PANGO_WEIGHT_NORMAL, -1);
3058
                                gtk_tree_path_free(path);
3059
                        }
3060
                }
3061
                valid = gtkut_tree_model_next(model, &iter);
3062
        }
3063
3064
        SORT_UNBLOCK(SORT_BY_UNREAD);
3065
3066
        trayicon_set_tooltip(NULL);
3067
        trayicon_set_notify(FALSE);
3068
        summary_status_show(summaryview);
3069
}
3070
3071
static void summary_mark_row_as_unread(SummaryView *summaryview,
3072
                                       GtkTreeIter *iter)
3073
{
3074
        MsgInfo *msginfo = NULL;
3075
3076
        GET_MSG_INFO(msginfo, iter);
3077
3078
        if (MSG_IS_DELETED(msginfo->flags)) {
3079
                msginfo->to_folder = NULL;
3080
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3081
                summaryview->deleted--;
3082
                summaryview->folder_item->mark_dirty = TRUE;
3083
        }
3084
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
3085
        if (!MSG_IS_UNREAD(msginfo->flags)) {
3086
                MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
3087
                summaryview->folder_item->unread++;
3088
                if (summaryview->on_filter)
3089
                        summaryview->flt_unread++;
3090
                summaryview->folder_item->mark_dirty = TRUE;
3091
                if (summaryview->folder_item->stype == F_VIRTUAL) {
3092
                        msginfo->folder->unread++;
3093
                        folderview_update_item(msginfo->folder, FALSE);
3094
                }
3095
                debug_print(_("Message %d is marked as unread\n"),
3096
                            msginfo->msgnum);
3097
        }
3098
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3099
        summary_set_row(summaryview, iter, msginfo);
3100
}
3101
3102
void summary_mark_as_unread(SummaryView *summaryview)
3103
{
3104
        GList *rows, *cur;
3105
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3106
        GtkTreeIter iter;
3107
        FolderSortKey sort_key = SORT_BY_NONE;
3108
        FolderSortType sort_type = SORT_ASCENDING;
3109
3110
        SORT_BLOCK(SORT_BY_UNREAD);
3111
3112
        rows = summary_get_selected_rows(summaryview);
3113
        for (cur = rows; cur != NULL; cur = cur->next) {
3114
                GtkTreePath *path = (GtkTreePath *)cur->data;
3115
3116
                gtk_tree_model_get_iter(model, &iter, path);
3117
                summary_mark_row_as_unread(summaryview, &iter);
3118
        }
3119
3120
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3121
                GSList *msglist;
3122
                msglist = summary_get_selected_msg_list(summaryview);
3123
                imap_msg_list_unset_perm_flags(msglist, MSG_REPLIED);
3124
                imap_msg_list_set_perm_flags(msglist, MSG_UNREAD);
3125
                g_slist_free(msglist);
3126
        }
3127
3128
        SORT_UNBLOCK(SORT_BY_UNREAD);
3129
3130
        summary_status_show(summaryview);
3131
}
3132
3133
static void summary_delete_row(SummaryView *summaryview, GtkTreeIter *iter)
3134
{
3135
        MsgInfo *msginfo = NULL;
3136
3137
        GET_MSG_INFO(msginfo, iter);
3138
3139
        if (MSG_IS_DELETED(msginfo->flags)) return;
3140
3141
        msginfo->to_folder = NULL;
3142
        if (MSG_IS_MOVE(msginfo->flags))
3143
                summaryview->moved--;
3144
        if (MSG_IS_COPY(msginfo->flags))
3145
                summaryview->copied--;
3146
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3147
        MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3148
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3149
        summaryview->deleted++;
3150
        summaryview->folder_item->mark_dirty = TRUE;
3151
3152
        if (!prefs_common.immediate_exec && 
3153
            summaryview->folder_item->stype != F_TRASH)
3154
                summary_set_row(summaryview, iter, msginfo);
3155
3156
        debug_print(_("Message %s/%d is set to delete\n"),
3157
                    msginfo->folder->path, msginfo->msgnum);
3158
}
3159
3160
static gboolean summary_delete_foreach_func(GtkTreeModel *model,
3161
                                            GtkTreePath *path,
3162
                                            GtkTreeIter *iter, gpointer data)
3163
{
3164
        summary_delete_row((SummaryView *)data, iter);
3165
        return FALSE;
3166
}
3167
3168
void summary_delete(SummaryView *summaryview)
3169
{
3170
        FolderItem *item = summaryview->folder_item;
3171
        GList *rows, *cur;
3172
        GtkTreeIter last_sel, next;
3173
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3174
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3175
3176
        if (!item || FOLDER_TYPE(item->folder) == F_NEWS) return;
3177
3178
        if (summary_is_locked(summaryview)) return;
3179
3180
        /* if current folder is trash, ask for confirmation */
3181
        if (item->stype == F_TRASH) {
3182
                AlertValue aval;
3183
3184
                aval = alertpanel(_("Delete message(s)"),
3185
                                  _("Do you really want to delete message(s) from the trash?"),
3186
                                  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3187
                if (aval != G_ALERTDEFAULT) return;
3188
        }
3189
3190
        rows = summary_get_selected_rows(summaryview);
3191
        if (!rows)
3192
                return;
3193
3194
        /* next code sets current row focus right. We need to find a row
3195
         * that is not deleted. */
3196
        for (cur = rows; cur != NULL; cur = cur->next) {
3197
                GtkTreePath *path = (GtkTreePath *)cur->data;
3198
3199
                gtk_tree_model_get_iter(model, &last_sel, path);
3200
                if (gtk_tree_model_iter_has_child(model, &last_sel) &&
3201
                    !gtk_tree_view_row_expanded(treeview, path)) {
3202
                        gtkut_tree_model_foreach
3203
                                (model, &last_sel, summary_delete_foreach_func,
3204
                                 summaryview);
3205
                } else
3206
                        summary_delete_row(summaryview, &last_sel);
3207
        }
3208
3209
        if (prefs_common.immediate_exec || item->stype == F_TRASH) {
3210
                summary_execute(summaryview);
3211
        } else {
3212
                if (summary_find_nearest_msg(summaryview, &next, &last_sel)) {
3213
                        summary_select_row
3214
                                (summaryview, &next,
3215
                                 messageview_is_visible
3216
                                        (summaryview->messageview),
3217
                                 FALSE);
3218
                }
3219
                summary_status_show(summaryview);
3220
        }
3221
}
3222
3223
static gboolean summary_delete_duplicated_func(GtkTreeModel *model,
3224
                                               GtkTreePath *path,
3225
                                               GtkTreeIter *iter,
3226
                                               gpointer data)
3227
{
3228
        SummaryView *summaryview = (SummaryView *)data;
3229
        MsgInfo *msginfo;
3230
        GtkTreeIter *found;
3231
        GtkTreePath *found_path;
3232
3233
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3234
3235
        if (!msginfo || !msginfo->msgid || !*msginfo->msgid)
3236
                return FALSE;
3237
3238
        found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
3239
3240
        if (found) {
3241
                found_path = gtk_tree_model_get_path(model, found);
3242
                if (gtk_tree_path_compare(path, found_path) != 0)
3243
                        summary_delete_row(summaryview, iter);
3244
                gtk_tree_path_free(found_path);
3245
        }
3246
3247
        return FALSE;
3248
}
3249
3250
void summary_delete_duplicated(SummaryView *summaryview)
3251
{
3252
        if (!summaryview->folder_item ||
3253
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3254
        if (summaryview->folder_item->stype == F_TRASH) return;
3255
3256
        main_window_cursor_wait(summaryview->mainwin);
3257
        debug_print("Deleting duplicated messages...");
3258
        STATUSBAR_PUSH(summaryview->mainwin,
3259
                       _("Deleting duplicated messages..."));
3260
3261
        summary_msgid_table_create(summaryview);
3262
3263
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
3264
                               summary_delete_duplicated_func, summaryview);
3265
3266
        summary_msgid_table_destroy(summaryview);
3267
3268
        if (prefs_common.immediate_exec)
3269
                summary_execute(summaryview);
3270
        else
3271
                summary_status_show(summaryview);
3272
3273
        debug_print("done.\n");
3274
        STATUSBAR_POP(summaryview->mainwin);
3275
        main_window_cursor_normal(summaryview->mainwin);
3276
}
3277
3278
static void summary_unmark_row(SummaryView *summaryview, GtkTreeIter *iter)
3279
{
3280
        MsgInfo *msginfo = NULL;
3281
3282
        GET_MSG_INFO(msginfo, iter);
3283
3284
        msginfo->to_folder = NULL;
3285
        if (MSG_IS_DELETED(msginfo->flags))
3286
                summaryview->deleted--;
3287
        if (MSG_IS_MOVE(msginfo->flags))
3288
                summaryview->moved--;
3289
        if (MSG_IS_COPY(msginfo->flags))
3290
                summaryview->copied--;
3291
        MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3292
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
3293
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3294
        summaryview->folder_item->mark_dirty = TRUE;
3295
        summary_set_row(summaryview, iter, msginfo);
3296
3297
        debug_print(_("Message %s/%d is unmarked\n"),
3298
                    msginfo->folder->path, msginfo->msgnum);
3299
}
3300
3301
void summary_unmark(SummaryView *summaryview)
3302
{
3303
        GList *rows, *cur;
3304
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3305
        GtkTreeIter iter;
3306
        FolderSortKey sort_key = SORT_BY_NONE;
3307
        FolderSortType sort_type = SORT_ASCENDING;
3308
3309
        SORT_BLOCK(SORT_BY_MARK);
3310
3311
        rows = summary_get_selected_rows(summaryview);
3312
        for (cur = rows; cur != NULL; cur = cur->next) {
3313
                GtkTreePath *path = (GtkTreePath *)cur->data;
3314
                gtk_tree_model_get_iter(model, &iter, path);
3315
                summary_unmark_row(summaryview, &iter);
3316
        }
3317
3318
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_IMAP) {
3319
                GSList *msglist;
3320
3321
                msglist = summary_get_selected_msg_list(summaryview);
3322
                imap_msg_list_unset_perm_flags(msglist, MSG_MARKED);
3323
                g_slist_free(msglist);
3324
        }
3325
3326
        SORT_UNBLOCK(SORT_BY_MARK);
3327
3328
        summary_status_show(summaryview);
3329
}
3330
3331
static void summary_move_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3332
                                FolderItem *to_folder)
3333
{
3334
        MsgInfo *msginfo;
3335
3336
        g_return_if_fail(to_folder != NULL);
3337
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3338
                return;
3339
3340
        GET_MSG_INFO(msginfo, iter);
3341
3342
        msginfo->to_folder = to_folder;
3343
        if (MSG_IS_DELETED(msginfo->flags)) {
3344
                summaryview->deleted--;
3345
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3346
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3347
        }
3348
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3349
        if (!MSG_IS_MOVE(msginfo->flags)) {
3350
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3351
                summaryview->moved++;
3352
        }
3353
        summaryview->folder_item->mark_dirty = TRUE;
3354
        if (!prefs_common.immediate_exec)
3355
                summary_set_row(summaryview, iter, msginfo);
3356
3357
        debug_print(_("Message %d is set to move to %s\n"),
3358
                    msginfo->msgnum, to_folder->path);
3359
}
3360
3361
static gboolean summary_move_foreach_func(GtkTreeModel *model,
3362
                                          GtkTreePath *path, GtkTreeIter *iter,
3363
                                          gpointer data)
3364
{
3365
        SummaryView *summaryview = (SummaryView *)data;
3366
3367
        summary_move_row_to(summaryview, iter, summaryview->to_folder);
3368
        return FALSE;
3369
}
3370
3371
void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3372
{
3373
        GList *rows, *cur;
3374
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3375
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3376
        GtkTreeIter iter;
3377
3378
        if (!to_folder) return;
3379
        if (!summaryview->folder_item ||
3380
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
3381
                return;
3382
        if (summaryview->folder_item->stype == F_QUEUE ||
3383
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3384
                return;
3385
3386
        if (summary_is_locked(summaryview)) return;
3387
3388
        if (summaryview->folder_item == to_folder) {
3389
                alertpanel_warning(_("Destination is same as current folder."));
3390
                return;
3391
        }
3392
3393
        rows = summary_get_selected_rows(summaryview);
3394
        for (cur = rows; cur != NULL; cur = cur->next) {
3395
                GtkTreePath *path = (GtkTreePath *)cur->data;
3396
3397
                gtk_tree_model_get_iter(model, &iter, path);
3398
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3399
                    !gtk_tree_view_row_expanded(treeview, path)) {
3400
                        summaryview->to_folder = to_folder;
3401
                        gtkut_tree_model_foreach
3402
                                (model, &iter, summary_move_foreach_func,
3403
                                 summaryview);
3404
                        summaryview->to_folder = NULL;
3405
                } else
3406
                        summary_move_row_to(summaryview, &iter, to_folder);
3407
        }
3408
3409
        if (prefs_common.immediate_exec)
3410
                summary_execute(summaryview);
3411
        else {
3412
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3413
                summary_status_show(summaryview);
3414
        }
3415
}
3416
3417
void summary_move_to(SummaryView *summaryview)
3418
{
3419
        FolderItem *to_folder;
3420
3421
        if (!summaryview->folder_item ||
3422
            FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
3423
3424
        to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
3425
                                         FOLDER_SEL_MOVE, NULL);
3426
        summary_move_selected_to(summaryview, to_folder);
3427
}
3428
3429
static void summary_copy_row_to(SummaryView *summaryview, GtkTreeIter *iter,
3430
                                FolderItem *to_folder)
3431
{
3432
        MsgInfo *msginfo;
3433
3434
        g_return_if_fail(to_folder != NULL);
3435
        if (to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3436
                return;
3437
3438
        GET_MSG_INFO(msginfo, iter);
3439
3440
        msginfo->to_folder = to_folder;
3441
        if (MSG_IS_DELETED(msginfo->flags)) {
3442
                summaryview->deleted--;
3443
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
3444
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
3445
        }
3446
        MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3447
        if (!MSG_IS_COPY(msginfo->flags)) {
3448
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3449
                summaryview->copied++;
3450
        }
3451
        summaryview->folder_item->mark_dirty = TRUE;
3452
        if (!prefs_common.immediate_exec)
3453
                summary_set_row(summaryview, iter, msginfo);
3454
3455
        debug_print(_("Message %d is set to copy to %s\n"),
3456
                    msginfo->msgnum, to_folder->path);
3457
}
3458
3459
static gboolean summary_copy_foreach_func(GtkTreeModel *model,
3460
                                          GtkTreePath *path, GtkTreeIter *iter,
3461
                                          gpointer data)
3462
{
3463
        SummaryView *summaryview = (SummaryView *)data;
3464
3465
        summary_copy_row_to(summaryview, iter, summaryview->to_folder);
3466
        return FALSE;
3467
}
3468
3469
void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3470
{
3471
        GList *rows, *cur;
3472
        GtkTreeView *treeview = GTK_TREE_VIEW(summaryview->treeview);
3473
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3474
        GtkTreeIter iter;
3475
3476
        if (!to_folder) return;
3477
        if (!summaryview->folder_item) return;
3478
        if (summaryview->folder_item->stype == F_QUEUE ||
3479
            to_folder->stype == F_QUEUE || to_folder->stype == F_VIRTUAL)
3480
                return;
3481
3482
        if (summary_is_locked(summaryview)) return;
3483
3484
        if (summaryview->folder_item == to_folder) {
3485
                alertpanel_warning
3486
                        (_("Destination for copy is same as current folder."));
3487
                return;
3488
        }
3489
3490
        rows = summary_get_selected_rows(summaryview);
3491
        for (cur = rows; cur != NULL; cur = cur->next) {
3492
                GtkTreePath *path = (GtkTreePath *)cur->data;
3493
3494
                gtk_tree_model_get_iter(model, &iter, path);
3495
                if (gtk_tree_model_iter_has_child(model, &iter) &&
3496
                    !gtk_tree_view_row_expanded(treeview, path)) {
3497
                        summaryview->to_folder = to_folder;
3498
                        gtkut_tree_model_foreach
3499
                                 (model, &iter, summary_copy_foreach_func,
3500
                                  summaryview);
3501
                        summaryview->to_folder = NULL;
3502
                } else
3503
                        summary_copy_row_to(summaryview, &iter, to_folder);
3504
        }
3505
3506
        if (prefs_common.immediate_exec)
3507
                summary_execute(summaryview);
3508
        else {
3509
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3510
                summary_status_show(summaryview);
3511
        }
3512
}
3513
3514
void summary_copy_to(SummaryView *summaryview)
3515
{
3516
        FolderItem *to_folder;
3517
3518
        if (!summaryview->folder_item) return;
3519
3520
        to_folder = foldersel_folder_sel(summaryview->folder_item->folder,
3521
                                         FOLDER_SEL_COPY, NULL);
3522
        summary_copy_selected_to(summaryview, to_folder);
3523
}
3524
3525
void summary_add_address(SummaryView *summaryview)
3526
{
3527
        GtkTreeIter iter;
3528
        MsgInfo *msginfo = NULL;
3529
        gchar *from;
3530
3531
        if (!summaryview->selected) return;
3532
3533
        if (!gtkut_tree_row_reference_get_iter
3534
                (GTK_TREE_MODEL(summaryview->store),
3535
                 summaryview->selected, &iter))
3536
                return;
3537
3538
        GET_MSG_INFO(msginfo, &iter);
3539
        Xstrdup_a(from, msginfo->from, return);
3540
        eliminate_address_comment(from);
3541
        extract_address(from);
3542
        addressbook_add_contact(msginfo->fromname, from, NULL);
3543
}
3544
3545
void summary_select_all(SummaryView *summaryview)
3546
{
3547
        gtk_tree_selection_select_all(summaryview->selection);
3548
}
3549
3550
void summary_unselect_all(SummaryView *summaryview)
3551
{
3552
        gtk_tree_selection_unselect_all(summaryview->selection);
3553
}
3554
3555
void summary_select_thread(SummaryView *summaryview)
3556
{
3557
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3558
        GtkTreeIter iter, parent, child, next;
3559
        GtkTreePath *start_path, *end_path;
3560
        gboolean valid;
3561
3562
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
3563
                                                  &iter);
3564
        if (!valid)
3565
                return;
3566
3567
        while (gtk_tree_model_iter_parent(model, &parent, &iter))
3568
                iter = parent;
3569
3570
        if (!gtk_tree_model_iter_children(model, &child, &iter))
3571
                return;
3572
3573
        start_path = gtk_tree_model_get_path(model, &iter);
3574
3575
        for (;;) {
3576
                next = iter = child;
3577
                while (gtk_tree_model_iter_next(model, &next))
3578
                        iter = next;
3579
                if (!gtk_tree_model_iter_children(model, &child, &iter))
3580
                        break;
3581
        }
3582
3583
        end_path = gtk_tree_model_get_path(model, &iter);
3584
3585
        gtk_tree_view_expand_row(GTK_TREE_VIEW(summaryview->treeview),
3586
                                 start_path, TRUE);
3587
        gtk_tree_selection_select_range(summaryview->selection,
3588
                                        start_path, end_path);
3589
3590
        gtk_tree_path_free(end_path);
3591
        gtk_tree_path_free(start_path);
3592
}
3593
3594
void summary_save_as(SummaryView *summaryview)
3595
{
3596
        GtkTreeIter iter;
3597
        MsgInfo *msginfo = NULL;
3598
        gchar *filename = NULL;
3599
        gchar *src, *dest;
3600
3601
        if (!summaryview->selected) return;
3602
        if (!gtkut_tree_row_reference_get_iter
3603
                (GTK_TREE_MODEL(summaryview->store),
3604
                 summaryview->selected, &iter))
3605
                return;
3606
3607
        GET_MSG_INFO(msginfo, &iter);
3608
        if (!msginfo) return;
3609
3610
        if (msginfo->subject) {
3611
                Xstrdup_a(filename, msginfo->subject, return);
3612
                subst_for_filename(filename);
3613
        }
3614
3615
        dest = filesel_save_as(filename);
3616
        if (!dest) return;
3617
3618
        src = procmsg_get_message_file(msginfo);
3619
        if (copy_file(src, dest, TRUE) < 0) {
3620
                gchar *utf8_dest;
3621
3622
                utf8_dest = conv_filename_to_utf8(dest);
3623
                alertpanel_error(_("Can't save the file `%s'."),
3624
                                 g_basename(utf8_dest));
3625
                g_free(utf8_dest);
3626
        }
3627
        g_free(src);
3628
3629
        g_free(dest);
3630
}
3631
3632
void summary_print(SummaryView *summaryview)
3633
{
3634
        GSList *mlist;
3635
        gboolean all_headers;
3636
3637
        all_headers = summaryview->messageview->textview->show_all_headers;
3638
        mlist = summary_get_selected_msg_list(summaryview);
3639
        if (!mlist)
3640
                return;
3641
        printing_print_messages(mlist, all_headers);
3642
        g_slist_free(mlist);
3643
}
3644
3645
gboolean summary_execute(SummaryView *summaryview)
3646
{
3647
        gint val = 0;
3648
3649
        if (!summaryview->folder_item) return FALSE;
3650
3651
        if (summary_is_locked(summaryview)) return FALSE;
3652
        summary_lock(summaryview);
3653
3654
        val |= summary_execute_move(summaryview);
3655
        val |= summary_execute_copy(summaryview);
3656
        val |= summary_execute_delete(summaryview);
3657
3658
        summary_unlock(summaryview);
3659
3660
        summary_remove_invalid_messages(summaryview);
3661
3662
        statusbar_pop_all();
3663
        STATUSBAR_POP(summaryview->mainwin);
3664
3665
        if (val != 0) {
3666
                alertpanel_error(_("Error occurred while processing messages."));
3667
        }
3668
3669
        return TRUE;
3670
}
3671
3672
static void summary_remove_invalid_messages(SummaryView *summaryview)
3673
{
3674
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3675
        MsgInfo *disp_msginfo = NULL, *msginfo;
3676
        FolderItem *item = summaryview->folder_item;
3677
        GtkTreeIter iter, next;
3678
        GtkTreePath *path;
3679
        gboolean valid;
3680
3681
        /* get currently displayed message */
3682
        if (summaryview->displayed) {
3683
                GtkTreeIter displayed;
3684
3685
                valid = gtkut_tree_row_reference_get_iter
3686
                        (model, summaryview->displayed, &displayed);
3687
                if (valid) {
3688
                        gtk_tree_model_get(model, &displayed,
3689
                                           S_COL_MSG_INFO, &disp_msginfo, -1);
3690
                        if (MSG_IS_INVALID(disp_msginfo->flags)) {
3691
                                valid = FALSE;
3692
                                disp_msginfo = NULL;
3693
                        }
3694
                }
3695
                if (!valid) {
3696
                        /* g_print("displayed became invalid before removing\n"); */
3697
                        messageview_clear(summaryview->messageview);
3698
                        gtk_tree_row_reference_free(summaryview->displayed);
3699
                        summaryview->displayed = NULL;
3700
                }
3701
        }
3702
3703
        if (item->threaded)
3704
                summary_modify_threads(summaryview);
3705
3706
        /* update selection */
3707
        valid = gtkut_tree_row_reference_get_iter(model, summaryview->selected,
3708
                                                  &iter);
3709
        if (valid) {
3710
                valid = summary_find_nearest_msg(summaryview, &next, &iter);
3711
                if (valid) {
3712
                        gtk_tree_model_get(model, &next,
3713
                                           S_COL_MSG_INFO, &msginfo, -1);
3714
                        if (disp_msginfo && disp_msginfo == msginfo) {
3715
                                /* g_print("replace displayed\n"); */
3716
                                path = gtk_tree_model_get_path(model, &next);
3717
                                gtk_tree_row_reference_free
3718
                                        (summaryview->displayed);
3719
                                summaryview->displayed =
3720
                                        gtk_tree_row_reference_new(model, path);
3721
                                gtk_tree_path_free(path);
3722
                        }
3723
                        summary_select_row
3724
                                (summaryview, &next,
3725
                                 (prefs_common.immediate_exec &&
3726
                                  messageview_is_visible
3727
                                        (summaryview->messageview)),
3728
                                 FALSE);
3729
                }
3730
        }
3731
        if (!valid)
3732
                summary_unselect_all(summaryview);
3733
3734
        for (valid = gtk_tree_model_get_iter_first(model, &iter);
3735
             valid == TRUE; iter = next) {
3736
                next = iter;
3737
                valid = gtkut_tree_model_next(model, &next);
3738
3739
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
3740
                if (!MSG_IS_INVALID(msginfo->flags))
3741
                        continue;
3742
3743
                if (gtk_tree_model_iter_has_child(model, &iter)) {
3744
                        g_warning("summary_remove_invalid_messages(): "
3745
                                  "tried to remove row which has child\n");
3746
                        continue;
3747
                }
3748
3749
                gtk_tree_store_remove(GTK_TREE_STORE(model), &iter);
3750
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
3751
                                                        msginfo);
3752
                if (summaryview->flt_mlist)
3753
                        summaryview->flt_mlist =
3754
                                g_slist_remove(summaryview->flt_mlist, msginfo);
3755
                procmsg_msginfo_free(msginfo);
3756
3757
                item->cache_dirty = TRUE;
3758
        }
3759
3760
        /* selection list becomes invalid if modified */
3761
        if (item->cache_dirty)
3762
                summary_selection_list_free(summaryview);
3763
3764
        if (summaryview->displayed &&
3765
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
3766
                /* g_print("displayed became invalid after removing. searching disp_msginfo...\n"); */
3767
                if (disp_msginfo &&
3768
                    gtkut_tree_model_find_by_column_data
3769
                        (model, &iter, NULL, S_COL_MSG_INFO, disp_msginfo)) {
3770
                        /* g_print("replace displayed\n"); */
3771
                        path = gtk_tree_model_get_path(model, &iter);
3772
                        gtk_tree_row_reference_free(summaryview->displayed);
3773
                        summaryview->displayed =
3774
                                gtk_tree_row_reference_new(model, path);
3775
                        gtk_tree_path_free(path);
3776
                } else {
3777
                        messageview_clear(summaryview->messageview);
3778
                        gtk_tree_row_reference_free(summaryview->displayed);
3779
                        summaryview->displayed = NULL;
3780
                }
3781
        }
3782
3783
        if (gtk_tree_model_get_iter_first(model, &iter))
3784
                gtk_widget_grab_focus(summaryview->treeview);
3785
        else {
3786
                menu_set_insensitive_all
3787
                        (GTK_MENU_SHELL(summaryview->popupmenu));
3788
                gtk_widget_grab_focus(summaryview->folderview->treeview);
3789
        }
3790
3791
        summary_write_cache(summaryview);
3792
3793
        summary_update_status(summaryview);
3794
        summary_status_show(summaryview);
3795
}
3796
3797
static gboolean summary_execute_move_func(GtkTreeModel *model,
3798
                                          GtkTreePath *path, GtkTreeIter *iter,
3799
                                          gpointer data)
3800
{
3801
        SummaryView *summaryview = data;
3802
        MsgInfo *msginfo;
3803
3804
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3805
3806
        if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
3807
                g_hash_table_insert(summaryview->folder_table,
3808
                                    msginfo->to_folder, GINT_TO_POINTER(1));
3809
3810
                summaryview->tmp_mlist =
3811
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
3812
3813
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3814
                summary_set_row(summaryview, iter, msginfo);
3815
        }
3816
3817
        return FALSE;
3818
}
3819
3820
static gint summary_execute_move(SummaryView *summaryview)
3821
{
3822
        gint val = 0;
3823
3824
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
3825
3826
        /* search moving messages and execute */
3827
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
3828
                               summary_execute_move_func, summaryview);
3829
3830
        if (summaryview->tmp_mlist) {
3831
                summaryview->tmp_mlist =
3832
                        g_slist_reverse(summaryview->tmp_mlist);
3833
                val = procmsg_move_messages(summaryview->tmp_mlist);
3834
3835
                folderview_update_item_foreach(summaryview->folder_table,
3836
                                               FALSE);
3837
3838
                g_slist_free(summaryview->tmp_mlist);
3839
                summaryview->tmp_mlist = NULL;
3840
        }
3841
3842
        g_hash_table_destroy(summaryview->folder_table);
3843
        summaryview->folder_table = NULL;
3844
3845
        return val;
3846
}
3847
3848
static gboolean summary_execute_copy_func(GtkTreeModel *model,
3849
                                          GtkTreePath *path, GtkTreeIter *iter,
3850
                                          gpointer data)
3851
{
3852
        SummaryView *summaryview = data;
3853
        MsgInfo *msginfo;
3854
3855
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3856
3857
        if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
3858
                g_hash_table_insert(summaryview->folder_table,
3859
                                    msginfo->to_folder, GINT_TO_POINTER(1));
3860
3861
                summaryview->tmp_mlist =
3862
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
3863
3864
                MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3865
                summary_set_row(summaryview, iter, msginfo);
3866
        }
3867
3868
        return FALSE;
3869
}
3870
3871
static gint summary_execute_copy(SummaryView *summaryview)
3872
{
3873
        gint val = 0;
3874
3875
        summaryview->folder_table = g_hash_table_new(NULL, NULL);
3876
3877
        /* search copying messages and execute */
3878
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
3879
                               summary_execute_copy_func, summaryview);
3880
3881
        if (summaryview->tmp_mlist) {
3882
                summaryview->tmp_mlist =
3883
                        g_slist_reverse(summaryview->tmp_mlist);
3884
                val = procmsg_copy_messages(summaryview->tmp_mlist);
3885
3886
                folderview_update_item_foreach(summaryview->folder_table,
3887
                                               FALSE);
3888
3889
                g_slist_free(summaryview->tmp_mlist);
3890
                summaryview->tmp_mlist = NULL;
3891
        }
3892
3893
        g_hash_table_destroy(summaryview->folder_table);
3894
        summaryview->folder_table = NULL;
3895
3896
        return val;
3897
}
3898
3899
static gboolean summary_execute_delete_func(GtkTreeModel *model,
3900
                                            GtkTreePath *path,
3901
                                            GtkTreeIter *iter, gpointer data)
3902
{
3903
        SummaryView *summaryview = data;
3904
        MsgInfo *msginfo;
3905
3906
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
3907
3908
        if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
3909
                summaryview->tmp_mlist =
3910
                        g_slist_prepend(summaryview->tmp_mlist, msginfo);
3911
        }
3912
3913
        return FALSE;
3914
}
3915
3916
static gint summary_execute_delete(SummaryView *summaryview)
3917
{
3918
        FolderItem *trash;
3919
        gint val = 0;
3920
3921
        trash = summaryview->folder_item->folder->trash;
3922
        if (FOLDER_TYPE(summaryview->folder_item->folder) == F_MH) {
3923
                g_return_val_if_fail(trash != NULL, 0);
3924
        }
3925
3926
        /* search deleting messages and execute */
3927
        gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store), 
3928
                               summary_execute_delete_func, summaryview);
3929
3930
        if (!summaryview->tmp_mlist) return 0;
3931
3932
        summaryview->tmp_mlist = g_slist_reverse(summaryview->tmp_mlist);
3933
3934
        if (summaryview->folder_item != trash)
3935
                val = folder_item_move_msgs(trash, summaryview->tmp_mlist);
3936
        else
3937
                val = folder_item_remove_msgs(trash, summaryview->tmp_mlist);
3938
3939
        g_slist_free(summaryview->tmp_mlist);
3940
        summaryview->tmp_mlist = NULL;
3941
3942
        if (summaryview->folder_item != trash) {
3943
                folder_item_scan(trash);
3944
                folderview_update_item(trash, FALSE);
3945
        }
3946
3947
        return val == -1 ? -1 : 0;
3948
}
3949
3950
/* thread functions */
3951
3952
void summary_thread_build(SummaryView *summaryview)
3953
{
3954
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
3955
        GtkTreeStore *store = summaryview->store;
3956
        GtkTreeIter iter, next;
3957
        GtkTreePath *path;
3958
        GSList *mlist;
3959
        GNode *root, *node;
3960
        GHashTable *node_table;
3961
        MsgInfo *msginfo;
3962
        gboolean valid;
3963
        FolderSortKey sort_key = SORT_BY_NONE;
3964
        FolderSortType sort_type = SORT_ASCENDING;
3965
        MsgInfo *selected_msg, *displayed_msg;
3966
3967
        if (!summaryview->folder_item)
3968
                return;
3969
3970
        summary_lock(summaryview);
3971
3972
        debug_print(_("Building threads..."));
3973
        STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
3974
        main_window_cursor_wait(summaryview->mainwin);
3975
3976
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
3977
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
3978
                                        0, 0, NULL, NULL, summaryview);
3979
3980
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
3981
        displayed_msg = summary_get_msginfo
3982
                (summaryview, summaryview->displayed);
3983
3984
        summaryview->folder_item->threaded = TRUE;
3985
3986
        mlist = summary_get_msg_list(summaryview);
3987
        root = procmsg_get_thread_tree(mlist);
3988
        node_table = g_hash_table_new(NULL, NULL);
3989
        for (node = root->children; node != NULL; node = node->next) {
3990
                g_hash_table_insert(node_table, node->data, node);
3991
        }
3992
3993
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
3994
                sort_key = summaryview->folder_item->sort_key;
3995
                sort_type = summaryview->folder_item->sort_type;
3996
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
3997
        }
3998
3999
        valid = gtk_tree_model_get_iter_first(model, &next);
4000
        while (valid) {
4001
                iter = next;
4002
                valid = gtk_tree_model_iter_next(model, &next);
4003
4004
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4005
                node = g_hash_table_lookup(node_table, msginfo);
4006
                if (node) {
4007
                        GNode *cur;
4008
                        GtkTreeIter child;
4009
                        guint tdate;
4010
4011
                        for (cur = node->children; cur != NULL;
4012
                             cur = cur->next) {
4013
                                summary_insert_gnode(summaryview, store, &child,
4014
                                                     &iter, NULL, cur);
4015
                        }
4016
4017
                        tdate = procmsg_get_thread_date(node);
4018
                        gtk_tree_store_set(store, &iter, S_COL_TDATE, tdate, -1);
4019
                } else
4020
                        gtk_tree_store_remove(store, &iter);
4021
        }
4022
4023
        if (sort_key != SORT_BY_NONE)
4024
                summary_sort(summaryview, sort_key, sort_type);
4025
4026
        g_hash_table_destroy(node_table);
4027
        g_node_destroy(root);
4028
        g_slist_free(mlist);
4029
4030
        if (prefs_common.expand_thread)
4031
                gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4032
4033
        if (!summaryview->selected ||
4034
            (summaryview->selected &&
4035
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4036
                if (selected_msg &&
4037
                    gtkut_tree_model_find_by_column_data
4038
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4039
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4040
                }
4041
        } else
4042
                summary_scroll_to_selected(summaryview, TRUE);
4043
4044
        if (summaryview->displayed &&
4045
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4046
                if (displayed_msg &&
4047
                    gtkut_tree_model_find_by_column_data
4048
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4049
                        path = gtk_tree_model_get_path(model, &iter);
4050
                        gtk_tree_row_reference_free(summaryview->displayed);
4051
                        summaryview->displayed =
4052
                                gtk_tree_row_reference_new(model, path);
4053
                        gtk_tree_path_free(path);
4054
                } else {
4055
                        messageview_clear(summaryview->messageview);
4056
                        gtk_tree_row_reference_free(summaryview->displayed);
4057
                        summaryview->displayed = NULL;
4058
                }
4059
        }
4060
4061
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4062
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4063
                                          0, 0, NULL, NULL, summaryview);
4064
4065
        debug_print(_("done.\n"));
4066
        STATUSBAR_POP(summaryview->mainwin);
4067
        main_window_cursor_normal(summaryview->mainwin);
4068
4069
        summary_unlock(summaryview);
4070
}
4071
4072
static void summary_unthread_node_recursive(SummaryView *summaryview,
4073
                                            GtkTreeIter *iter,
4074
                                            GtkTreeIter *sibling)
4075
{
4076
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4077
        GtkTreeIter iter_, child;
4078
        MsgInfo *msginfo;
4079
        gboolean valid;
4080
4081
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4082
        gtk_tree_store_insert_after(GTK_TREE_STORE(model), &iter_,
4083
                                    NULL, sibling);
4084
        summary_set_row(summaryview, &iter_, msginfo);
4085
        *sibling = iter_;
4086
4087
        valid = gtk_tree_model_iter_children(model, &child, iter);
4088
        while (valid) {
4089
                summary_unthread_node_recursive(summaryview, &child, sibling);
4090
                valid = gtk_tree_model_iter_next(model, &child);
4091
        }
4092
}
4093
4094
static void summary_unthread_node(SummaryView *summaryview, GtkTreeIter *iter)
4095
{
4096
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4097
        GtkTreeIter child, sibling, next;
4098
        gboolean valid;
4099
4100
        sibling = *iter;
4101
4102
        valid = gtk_tree_model_iter_children(model, &next, iter);
4103
        while (valid) {
4104
                child = next;
4105
                valid = gtk_tree_model_iter_next(model, &next);
4106
                summary_unthread_node_recursive(summaryview, &child, &sibling);
4107
                gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
4108
        }
4109
}
4110
4111
void summary_unthread(SummaryView *summaryview)
4112
{
4113
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4114
        GtkTreeIter iter, next;
4115
        GtkTreePath *path;
4116
        gboolean valid;
4117
        FolderSortKey sort_key = SORT_BY_NONE;
4118
        FolderSortType sort_type = SORT_ASCENDING;
4119
        MsgInfo *selected_msg, *displayed_msg;
4120
4121
        summary_lock(summaryview);
4122
4123
        debug_print(_("Unthreading..."));
4124
        STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
4125
        main_window_cursor_wait(summaryview->mainwin);
4126
4127
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
4128
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4129
                                        0, 0, NULL, NULL, summaryview);
4130
4131
        selected_msg = summary_get_msginfo(summaryview, summaryview->selected);
4132
        displayed_msg = summary_get_msginfo
4133
                (summaryview, summaryview->displayed);
4134
4135
        if (summaryview->folder_item)
4136
                summaryview->folder_item->threaded = FALSE;
4137
4138
        if (summaryview->folder_item->sort_key != SORT_BY_NONE) {
4139
                sort_key = summaryview->folder_item->sort_key;
4140
                sort_type = summaryview->folder_item->sort_type;
4141
                summary_sort(summaryview, SORT_BY_NONE, SORT_ASCENDING);
4142
        }
4143
4144
        valid = gtk_tree_model_get_iter_first(model, &next);
4145
        while (valid) {
4146
                iter = next;
4147
                valid = gtk_tree_model_iter_next(model, &next);
4148
                gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
4149
                                   S_COL_TDATE, 0, -1);
4150
        }
4151
4152
        valid = gtk_tree_model_get_iter_first(model, &next);
4153
        while (valid) {
4154
                iter = next;
4155
                valid = gtk_tree_model_iter_next(model, &next);
4156
                summary_unthread_node(summaryview, &iter);
4157
        }
4158
4159
        if (sort_key != SORT_BY_NONE)
4160
                summary_sort(summaryview, sort_key, sort_type);
4161
4162
        if (!summaryview->selected ||
4163
            (summaryview->selected &&
4164
             !gtk_tree_row_reference_valid(summaryview->selected))) {
4165
                if (selected_msg &&
4166
                    gtkut_tree_model_find_by_column_data
4167
                        (model, &iter, NULL, S_COL_MSG_INFO, selected_msg)) {
4168
                        summary_select_row(summaryview, &iter, FALSE, TRUE);
4169
                }
4170
        } else
4171
                summary_scroll_to_selected(summaryview, TRUE);
4172
4173
        if (summaryview->displayed &&
4174
            !gtk_tree_row_reference_valid(summaryview->displayed)) {
4175
                if (displayed_msg &&
4176
                    gtkut_tree_model_find_by_column_data
4177
                        (model, &iter, NULL, S_COL_MSG_INFO, displayed_msg)) {
4178
                        path = gtk_tree_model_get_path(model, &iter);
4179
                        gtk_tree_row_reference_free(summaryview->displayed);
4180
                        summaryview->displayed =
4181
                                gtk_tree_row_reference_new(model, path);
4182
                        gtk_tree_path_free(path);
4183
                } else {
4184
                        messageview_clear(summaryview->messageview);
4185
                        gtk_tree_row_reference_free(summaryview->displayed);
4186
                        summaryview->displayed = NULL;
4187
                }
4188
        }
4189
4190
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
4191
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
4192
                                          0, 0, NULL, NULL, summaryview);
4193
4194
        debug_print(_("done.\n"));
4195
        STATUSBAR_POP(summaryview->mainwin);
4196
        main_window_cursor_normal(summaryview->mainwin);
4197
4198
        summary_unlock(summaryview);
4199
}
4200
4201
static gboolean summary_has_invalid_node(GtkTreeModel *model, GtkTreeIter *iter)
4202
{
4203
        GtkTreeIter child;
4204
        MsgInfo *msginfo;
4205
        gboolean valid;
4206
4207
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4208
        if (MSG_IS_INVALID(msginfo->flags))
4209
                return TRUE;
4210
4211
        valid = gtk_tree_model_iter_children(model, &child, iter);
4212
        while (valid) {
4213
                if (summary_has_invalid_node(model, &child))
4214
                        return TRUE;
4215
                valid = gtk_tree_model_iter_next(model, &child);
4216
        }
4217
4218
        return FALSE;
4219
}
4220
4221
static GNode *summary_get_modified_node(SummaryView *summaryview,
4222
                                        GtkTreeIter *iter,
4223
                                        GNode *parent, GNode *sibling)
4224
{
4225
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4226
        GNode *node = NULL, *new_sibling;
4227
        GtkTreeIter child;
4228
        MsgInfo *msginfo;
4229
        gboolean valid;
4230
4231
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4232
4233
        if (!MSG_IS_INVALID(msginfo->flags)) {
4234
                node = g_node_new(msginfo);
4235
                g_node_insert_after(parent, sibling, node);
4236
                parent = node;
4237
                sibling = NULL;
4238
        } else {
4239
                summaryview->all_mlist = g_slist_remove(summaryview->all_mlist,
4240
                                                        msginfo);
4241
                if (summaryview->flt_mlist)
4242
                        summaryview->flt_mlist =
4243
                                g_slist_remove(summaryview->flt_mlist, msginfo);
4244
                procmsg_msginfo_free(msginfo);
4245
        }
4246
4247
        valid = gtk_tree_model_iter_children(model, &child, iter);
4248
4249
        while (valid) {
4250
                new_sibling = summary_get_modified_node(summaryview, &child,
4251
                                                        parent, sibling);
4252
                if (new_sibling) {
4253
                        sibling = new_sibling;
4254
                        if (!node)
4255
                                node = sibling;
4256
                }
4257
                valid = gtk_tree_model_iter_next(model, &child);
4258
        }
4259
4260
        return node;
4261
}
4262
4263
#if 0
4264
static gboolean traverse(GNode *node, gpointer data)
4265
{
4266
        gint i;
4267
4268
        if (!node->data)
4269
                return FALSE;
4270
        for (i = 0; i < g_node_depth(node); i++)
4271
                g_print(" ");
4272
        g_print("%s\n", ((MsgInfo *)node->data)->subject);
4273
        return FALSE;
4274
}
4275
#endif
4276
4277
static void summary_modify_node(SummaryView *summaryview, GtkTreeIter *iter,
4278
                                GtkTreeIter *selected)
4279
{
4280
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4281
        MsgInfo *msginfo, *sel_msginfo = NULL;
4282
        GNode *root, *cur;
4283
        GtkTreeIter iter_, sibling;
4284
        GtkTreeIter *sibling_p = NULL;
4285
        GtkTreePath *path, *sel_path;
4286
        gboolean found = FALSE;
4287
4288
        if (!gtk_tree_model_iter_has_child(model, iter))
4289
                return;
4290
        if (!summary_has_invalid_node(model, iter))
4291
                return;
4292
4293
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4294
4295
        if (selected) {
4296
                path = gtk_tree_model_get_path(model, iter);
4297
                sel_path = gtk_tree_model_get_path(model, selected);
4298
                if (gtk_tree_path_compare(path, sel_path) == 0 ||
4299
                    gtk_tree_path_is_ancestor(path, sel_path))
4300
                        gtk_tree_model_get(model, selected,
4301
                                           S_COL_MSG_INFO, &sel_msginfo, -1);
4302
                gtk_tree_path_free(sel_path);
4303
                gtk_tree_path_free(path);
4304
        }
4305
4306
        root = g_node_new(NULL);
4307
        summary_get_modified_node(summaryview, iter, root, NULL);
4308
4309
        /* g_node_traverse(root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, traverse, NULL); */
4310
4311
        sibling = *iter;
4312
        if (gtk_tree_model_iter_next(model, &sibling))
4313
                sibling_p = &sibling;
4314
4315
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
4316
4317
        for (cur = root->children; cur != NULL; cur = cur->next) {
4318
                summary_insert_gnode_before(summaryview, GTK_TREE_STORE(model),
4319
                                            &iter_, NULL, sibling_p, cur);
4320
                if (summaryview->folder_item->threaded &&
4321
                    prefs_common.expand_thread) {
4322
                        path = gtk_tree_model_get_path(model, &iter_);
4323
                        gtk_tree_view_expand_row
4324
                                (GTK_TREE_VIEW(summaryview->treeview),
4325
                                 path, TRUE);
4326
                        gtk_tree_path_free(path);
4327
                }
4328
                if (sel_msginfo && !found) {
4329
                        found = gtkut_tree_model_find_by_column_data
4330
                                (model, selected, &iter_,
4331
                                 S_COL_MSG_INFO, sel_msginfo);
4332
                }
4333
        }
4334
4335
        g_node_destroy(root);
4336
4337
        summaryview->folder_item->cache_dirty = TRUE;
4338
}
4339
4340
static void summary_modify_threads(SummaryView *summaryview)
4341
{
4342
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4343
        GtkTreeIter iter, next, selected, new_selected;
4344
        GtkTreeIter *selected_p = NULL;
4345
        GtkTreePath *prev_path = NULL;
4346
        gboolean valid, has_selection;
4347
4348
        summary_lock(summaryview);
4349
4350
        debug_print("Modifying threads for execution...");
4351
4352
        g_signal_handlers_block_by_func(summaryview->selection,
4353
                                        summary_selection_changed, summaryview);
4354
4355
        has_selection = gtkut_tree_row_reference_get_iter
4356
                (model, summaryview->selected, &selected);
4357
        if (has_selection) {
4358
                prev_path = gtk_tree_row_reference_get_path
4359
                        (summaryview->selected);
4360
                if (summary_find_nearest_msg(summaryview, &next, &selected)) {
4361
                        selected = next;
4362
                        selected_p = &selected;
4363
                } else
4364
                        has_selection = FALSE;
4365
        }
4366
4367
        valid = gtk_tree_model_get_iter_first(model, &next);
4368
        while (valid) {
4369
                iter = next;
4370
                valid = gtk_tree_model_iter_next(model, &next);
4371
                summary_modify_node(summaryview, &iter, selected_p);
4372
        }
4373
4374
        g_signal_handlers_unblock_by_func(summaryview->selection,
4375
                                          summary_selection_changed,
4376
                                          summaryview);
4377
4378
        if (summaryview->folder_item->cache_dirty)
4379
                summary_selection_list_free(summaryview);
4380
4381
        if (has_selection &&
4382
            !gtk_tree_row_reference_valid(summaryview->selected)) {
4383
                if (prev_path &&
4384
                    gtk_tree_model_get_iter(model, &new_selected, prev_path))
4385
                        selected = new_selected;
4386
                summary_select_row(summaryview, &selected, FALSE, FALSE);
4387
        }
4388
        gtk_tree_path_free(prev_path);
4389
4390
        debug_print("done.\n");
4391
4392
        summary_unlock(summaryview);
4393
}
4394
4395
void summary_expand_threads(SummaryView *summaryview)
4396
{
4397
        gtk_tree_view_expand_all(GTK_TREE_VIEW(summaryview->treeview));
4398
}
4399
4400
void summary_collapse_threads(SummaryView *summaryview)
4401
{
4402
        gtk_tree_view_collapse_all(GTK_TREE_VIEW(summaryview->treeview));
4403
}
4404
4405
static gboolean summary_filter_func(GtkTreeModel *model, GtkTreePath *path,
4406
                                    GtkTreeIter *iter, gpointer data)
4407
{
4408
        SummaryView *summaryview = (SummaryView *)data;
4409
        MsgInfo *msginfo;
4410
        FilterInfo *fltinfo;
4411
4412
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4413
4414
        summaryview->flt_count++;
4415
        {
4416
                gchar msg[1024];
4417
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4418
                           summaryview->flt_count, summaryview->flt_total);
4419
                STATUSBAR_POP(summaryview->mainwin);
4420
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4421
                if ((summaryview->flt_count % 100) == 0) {
4422
                        GTK_EVENTS_FLUSH();
4423
                }
4424
        }
4425
4426
        fltinfo = filter_info_new();
4427
        fltinfo->flags = msginfo->flags;
4428
        filter_apply_msginfo(prefs_common.fltlist, msginfo, fltinfo);
4429
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4430
            fltinfo->actions[FLT_ACTION_COPY] ||
4431
            fltinfo->actions[FLT_ACTION_DELETE] ||
4432
            fltinfo->actions[FLT_ACTION_EXEC] ||
4433
            fltinfo->actions[FLT_ACTION_EXEC_ASYNC] ||
4434
            fltinfo->actions[FLT_ACTION_MARK] ||
4435
            fltinfo->actions[FLT_ACTION_COLOR_LABEL] ||
4436
            fltinfo->actions[FLT_ACTION_MARK_READ] ||
4437
            fltinfo->actions[FLT_ACTION_FORWARD] ||
4438
            fltinfo->actions[FLT_ACTION_FORWARD_AS_ATTACHMENT] ||
4439
            fltinfo->actions[FLT_ACTION_REDIRECT])
4440
                summaryview->filtered++;
4441
4442
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4443
                msginfo->flags = fltinfo->flags;
4444
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4445
                summaryview->folder_item->mark_dirty = TRUE;
4446
                summary_set_row(summaryview, iter, msginfo);
4447
                if (MSG_IS_IMAP(msginfo->flags)) {
4448
                        if (fltinfo->actions[FLT_ACTION_MARK])
4449
                                imap_msg_set_perm_flags(msginfo, MSG_MARKED);
4450
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4451
                                imap_msg_unset_perm_flags(msginfo,
4452
                                                          MSG_NEW|MSG_UNREAD);
4453
                }
4454
        }
4455
4456
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4457
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4458
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4459
                summary_delete_row(summaryview, iter);
4460
4461
        filter_info_free(fltinfo);
4462
4463
        return FALSE;
4464
}
4465
4466
static gboolean summary_filter_junk_func(GtkTreeModel *model, GtkTreePath *path,
4467
                                         GtkTreeIter *iter, gpointer data)
4468
{
4469
        SummaryView *summaryview = (SummaryView *)data;
4470
        MsgInfo *msginfo;
4471
        FilterInfo *fltinfo;
4472
4473
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4474
4475
        summaryview->flt_count++;
4476
        {
4477
                gchar msg[1024];
4478
                g_snprintf(msg, sizeof(msg), _("Filtering (%d / %d)..."),
4479
                           summaryview->flt_count, summaryview->flt_total);
4480
                STATUSBAR_POP(summaryview->mainwin);
4481
                STATUSBAR_PUSH(summaryview->mainwin, msg);
4482
                if ((summaryview->flt_count % 100) == 0) {
4483
                        GTK_EVENTS_FLUSH();
4484
                }
4485
        }
4486
4487
        fltinfo = filter_info_new();
4488
        fltinfo->flags = msginfo->flags;
4489
        filter_apply_msginfo(prefs_common.manual_junk_fltlist,
4490
                             msginfo, fltinfo);
4491
4492
        if (fltinfo->actions[FLT_ACTION_MOVE] ||
4493
            fltinfo->actions[FLT_ACTION_COPY] ||
4494
            fltinfo->actions[FLT_ACTION_DELETE] ||
4495
            fltinfo->actions[FLT_ACTION_MARK_READ])
4496
                summaryview->filtered++;
4497
        else if (fltinfo->error == FLT_ERROR_EXEC_FAILED) {
4498
                if (summaryview->flt_count == 1) {
4499
                        alertpanel_error
4500
                                (_("Execution of the junk filter command failed.\n"
4501
                                   "Please check the junk mail control setting."));
4502
                }
4503
                return TRUE;
4504
        }
4505
4506
        if (msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4507
                msginfo->flags = fltinfo->flags;
4508
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4509
                summaryview->folder_item->mark_dirty = TRUE;
4510
                summary_set_row(summaryview, iter, msginfo);
4511
                if (MSG_IS_IMAP(msginfo->flags)) {
4512
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4513
                                imap_msg_unset_perm_flags(msginfo,
4514
                                                          MSG_NEW|MSG_UNREAD);
4515
                }
4516
        }
4517
4518
        if (fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4519
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4520
        else if (fltinfo->actions[FLT_ACTION_DELETE])
4521
                summary_delete_row(summaryview, iter);
4522
4523
        filter_info_free(fltinfo);
4524
4525
        return FALSE;
4526
}
4527
4528
static void summary_filter_real(SummaryView *summaryview,
4529
                                GtkTreeModelForeachFunc func,
4530
                                gboolean selected_only)
4531
{
4532
        GList *rows;
4533
4534
        if (!summaryview->folder_item) return;
4535
4536
        summary_lock(summaryview);
4537
4538
        STATUSBAR_POP(summaryview->mainwin);
4539
4540
        debug_print(_("filtering..."));
4541
        STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
4542
        main_window_cursor_wait(summaryview->mainwin);
4543
        GTK_EVENTS_FLUSH();
4544
4545
        summaryview->filtered = 0;
4546
        summaryview->flt_count = 0;
4547
4548
        if (selected_only) {
4549
                rows = summary_get_selected_rows(summaryview);
4550
                summaryview->flt_total = g_list_length(rows);
4551
4552
                gtk_tree_selection_selected_foreach
4553
                        (summaryview->selection,
4554
                         (GtkTreeSelectionForeachFunc)func,
4555
                         summaryview);
4556
        } else {
4557
                summaryview->flt_total = summaryview->folder_item->total;
4558
                gtk_tree_model_foreach(GTK_TREE_MODEL(summaryview->store),
4559
                                       func, summaryview);
4560
        }
4561
4562
        summary_unlock(summaryview);
4563
4564
        if (prefs_common.immediate_exec)
4565
                summary_execute(summaryview);
4566
        else
4567
                summary_status_show(summaryview);
4568
4569
        folderview_update_all_updated(FALSE);
4570
4571
        debug_print(_("done.\n"));
4572
        STATUSBAR_POP(summaryview->mainwin);
4573
        main_window_cursor_normal(summaryview->mainwin);
4574
4575
        if (summaryview->filtered > 0) {
4576
                gchar result_msg[BUFFSIZE];
4577
                g_snprintf(result_msg, sizeof(result_msg),
4578
                           _("%d message(s) have been filtered."),
4579
                           summaryview->filtered);
4580
                STATUSBAR_PUSH(summaryview->mainwin, result_msg);
4581
        }
4582
        summaryview->filtered = 0;
4583
        summaryview->flt_count = 0;
4584
        summaryview->flt_total = 0;
4585
}
4586
4587
void summary_filter(SummaryView *summaryview, gboolean selected_only)
4588
{
4589
        if (prefs_common.fltlist)
4590
                summary_filter_real(summaryview, summary_filter_func,
4591
                                    selected_only);
4592
}
4593
4594
void summary_filter_junk(SummaryView *summaryview, gboolean selected_only)
4595
{
4596
        if (prefs_common.manual_junk_fltlist)
4597
                summary_filter_real(summaryview, summary_filter_junk_func,
4598
                                    selected_only);
4599
}
4600
4601
void summary_filter_open(SummaryView *summaryview, FilterCreateType type)
4602
{
4603
        GtkTreeIter iter;
4604
        MsgInfo *msginfo = NULL;
4605
        gchar *header = NULL;
4606
        gchar *key = NULL;
4607
4608
        if (!summaryview->selected) return;
4609
        if (!gtkut_tree_row_reference_get_iter
4610
                (GTK_TREE_MODEL(summaryview->store),
4611
                 summaryview->selected, &iter))
4612
                return;
4613
4614
        GET_MSG_INFO(msginfo, &iter);
4615
        if (!msginfo) return;
4616
4617
        filter_get_keyword_from_msg(msginfo, &header, &key, type);
4618
        prefs_filter_open(msginfo, header);
4619
4620
        g_free(header);
4621
        g_free(key);
4622
}
4623
4624
static void summary_junk_func(GtkTreeModel *model, GtkTreePath *path,
4625
                              GtkTreeIter *iter, gpointer data)
4626
{
4627
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
4628
        FilterAction action1 = {FLT_ACTION_EXEC, NULL, 0};
4629
        FilterAction action2 = {FLT_ACTION_MOVE, NULL, 0};
4630
        FilterAction action3 = {FLT_ACTION_MARK_READ, NULL, 0};
4631
        SummaryView *summaryview = (SummaryView *)data;
4632
        MsgInfo *msginfo;
4633
        FilterInfo *fltinfo;
4634
        gchar *file;
4635
        gint ret;
4636
4637
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4638
        file = procmsg_get_message_file(msginfo);
4639
        g_return_if_fail(file != NULL);
4640
4641
        action1.str_value = prefs_common.junk_learncmd;
4642
        action2.str_value = prefs_common.junk_folder;
4643
4644
        rule.action_list = g_slist_append(rule.action_list, &action1);
4645
        if (prefs_common.junk_folder)
4646
                rule.action_list = g_slist_append(rule.action_list, &action2);
4647
        if (prefs_common.mark_junk_as_read)
4648
                rule.action_list = g_slist_append(rule.action_list, &action3);
4649
4650
        fltinfo = filter_info_new();
4651
        fltinfo->flags = msginfo->flags;
4652
4653
        ret = filter_action_exec(&rule, msginfo, file, fltinfo);
4654
4655
        if (ret == 0 &&
4656
            msginfo->flags.perm_flags != fltinfo->flags.perm_flags) {
4657
                msginfo->flags = fltinfo->flags;
4658
                summary_set_row(summaryview, iter, msginfo);
4659
                if (MSG_IS_IMAP(msginfo->flags)) {
4660
                        if (fltinfo->actions[FLT_ACTION_MARK_READ])
4661
                                imap_msg_unset_perm_flags(msginfo,
4662
                                                          MSG_NEW | MSG_UNREAD);
4663
                }
4664
        }
4665
        if (ret == 0 && fltinfo->actions[FLT_ACTION_MOVE] && fltinfo->move_dest)
4666
                summary_move_row_to(summaryview, iter, fltinfo->move_dest);
4667
4668
        filter_info_free(fltinfo);
4669
        g_slist_free(rule.action_list);
4670
        g_free(file);
4671
}
4672
4673
static void summary_not_junk_func(GtkTreeModel *model, GtkTreePath *path,
4674
                                  GtkTreeIter *iter, gpointer data)
4675
{
4676
        FilterRule rule = {NULL, FLT_OR, NULL, NULL, FLT_TIMING_ANY, TRUE};
4677
        FilterAction action = {FLT_ACTION_EXEC, NULL, 0};
4678
        MsgInfo *msginfo;
4679
        FilterInfo *fltinfo;
4680
        gchar *file;
4681
        gint ret;
4682
4683
        gtk_tree_model_get(model, iter, S_COL_MSG_INFO, &msginfo, -1);
4684
        file = procmsg_get_message_file(msginfo);
4685
        g_return_if_fail(file != NULL);
4686
4687
        action.str_value = prefs_common.nojunk_learncmd;
4688
4689
        rule.action_list = g_slist_append(rule.action_list, &action);
4690
4691
        fltinfo = filter_info_new();
4692
4693
        ret = filter_action_exec(&rule, msginfo, file, fltinfo);
4694
4695
        filter_info_free(fltinfo);
4696
        g_slist_free(rule.action_list);
4697
        g_free(file);
4698
}
4699
4700
void summary_junk(SummaryView *summaryview)
4701
{
4702
        if (!prefs_common.enable_junk)
4703
                return;
4704
        if (!prefs_common.junk_learncmd)
4705
                return;
4706
4707
        summary_lock(summaryview);
4708
4709
        debug_print("Set mail as junk\n");
4710
4711
        gtk_tree_selection_selected_foreach(summaryview->selection,
4712
                                            summary_junk_func, summaryview);
4713
4714
        summary_unlock(summaryview);
4715
4716
        if (prefs_common.junk_folder && prefs_common.immediate_exec)
4717
                summary_execute(summaryview);
4718
        else {
4719
                summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
4720
                summary_status_show(summaryview);
4721
        }
4722
4723
        folderview_update_all_updated(FALSE);
4724
}
4725
4726
void summary_not_junk(SummaryView *summaryview)
4727
{
4728
        if (!prefs_common.enable_junk)
4729
                return;
4730
        if (!prefs_common.nojunk_learncmd)
4731
                return;
4732
4733
        summary_lock(summaryview);
4734
4735
        debug_print("Set mail as not junk\n");
4736
4737
        gtk_tree_selection_selected_foreach(summaryview->selection,
4738
                                            summary_not_junk_func, summaryview);
4739
4740
        summary_unlock(summaryview);
4741
}
4742
4743
void summary_reply(SummaryView *summaryview, ComposeMode mode)
4744
{
4745
        GSList *mlist;
4746
        MsgInfo *msginfo;
4747
        MsgInfo *displayed_msginfo = NULL;
4748
        gchar *text = NULL;
4749
4750
        mlist = summary_get_selected_msg_list(summaryview);
4751
        if (!mlist) return;
4752
        msginfo = (MsgInfo *)mlist->data;
4753
4754
        if (summaryview->displayed) {
4755
                GtkTreeIter iter;
4756
4757
                if (gtkut_tree_row_reference_get_iter
4758
                        (GTK_TREE_MODEL(summaryview->store),
4759
                         summaryview->displayed, &iter)) {
4760
                        GET_MSG_INFO(displayed_msginfo, &iter);
4761
                }
4762
        }
4763
4764
        /* use selection only if the displayed message is selected */
4765
        if (!mlist->next && msginfo == displayed_msginfo) {
4766
                TextView *textview;
4767
4768
                textview = messageview_get_current_textview
4769
                        (summaryview->messageview);
4770
                if (textview) {
4771
                        text = gtkut_text_view_get_selection
4772
                                (GTK_TEXT_VIEW(textview->text));
4773
                        if (text && *text == '\0') {
4774
                                g_free(text);
4775
                                text = NULL;
4776
                        }
4777
                }
4778
        }
4779
4780
        if (prefs_common.reply_with_quote)
4781
                mode |= COMPOSE_WITH_QUOTE;
4782
4783
        switch (COMPOSE_MODE(mode)) {
4784
        case COMPOSE_REPLY:
4785
        case COMPOSE_REPLY_TO_SENDER:
4786
        case COMPOSE_REPLY_TO_ALL:
4787
        case COMPOSE_REPLY_TO_LIST:
4788
                compose_reply(msginfo, summaryview->folder_item, mode, text);
4789
                break;
4790
        case COMPOSE_FORWARD:
4791
                compose_forward(mlist, summaryview->folder_item, FALSE, text);
4792
                break;
4793
        case COMPOSE_FORWARD_AS_ATTACH:
4794
                compose_forward(mlist, summaryview->folder_item, TRUE, NULL);
4795
                break;
4796
        case COMPOSE_REDIRECT:
4797
                compose_redirect(msginfo, summaryview->folder_item);
4798
                break;
4799
        default:
4800
                g_warning("summary_reply(): invalid mode: %d\n", mode);
4801
        }
4802
4803
        summary_update_selected_rows(summaryview);
4804
        g_free(text);
4805
        g_slist_free(mlist);
4806
}
4807
4808
/* color label */
4809
4810
#define N_COLOR_LABELS colorlabel_get_color_count()
4811
4812
static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
4813
                                                     gpointer data)
4814
{
4815
        guint color = GPOINTER_TO_UINT(data);
4816
        SummaryView *summaryview;
4817
4818
        summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
4819
        g_return_if_fail(summaryview != NULL);
4820
4821
        /* "dont_toggle" state set? */
4822
        if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
4823
                              "dont_toggle"))
4824
                return;
4825
4826
        summary_set_colorlabel(summaryview, color, NULL);
4827
}
4828
4829
/* summary_set_colorlabel() - labelcolor parameter is the color *flag*
4830
 * for the messsage; not the color index */
4831
void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
4832
                            GtkWidget *widget)
4833
{
4834
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
4835
        GList *rows, *cur;
4836
        GtkTreeIter iter;
4837
        MsgInfo *msginfo;
4838
        FolderSortKey sort_key = SORT_BY_NONE;
4839
        FolderSortType sort_type = SORT_ASCENDING;
4840
4841
        SORT_BLOCK(SORT_BY_LABEL);
4842
4843
        rows = summary_get_selected_rows(summaryview);
4844
        for (cur = rows; cur != NULL; cur = cur->next) {
4845
                gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
4846
                gtk_tree_model_get(model, &iter, S_COL_MSG_INFO, &msginfo, -1);
4847
4848
                MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK);
4849
                MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor);
4850
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_FLAG_CHANGED);
4851
                summary_set_row(summaryview, &iter, msginfo);
4852
        }
4853
4854
        if (rows)
4855
                summaryview->folder_item->mark_dirty = TRUE;
4856
4857
        SORT_UNBLOCK(SORT_BY_LABEL);
4858
}
4859
4860
static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menuitem,
4861
                                                          gpointer data)
4862
{
4863
        SummaryView *summaryview;
4864
        GtkMenuShell *menu;
4865
        GtkCheckMenuItem **items;
4866
        gint n;
4867
        GList *menu_cur;
4868
        GSList *mlist, *cur;
4869
4870
        summaryview = (SummaryView *)data;
4871
        g_return_if_fail(summaryview != NULL);
4872
4873
        menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
4874
        g_return_if_fail(menu != NULL);
4875
4876
        mlist = summary_get_selected_msg_list(summaryview);
4877
        if (!mlist) return;
4878
4879
        Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
4880
4881
        /* NOTE: don't return prematurely because we set the "dont_toggle"
4882
         * state for check menu items */
4883
        g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(1));
4884
4885
        /* clear items. get item pointers. */
4886
        for (n = 0, menu_cur = menu->children; menu_cur != NULL;
4887
             menu_cur = menu_cur->next) {
4888
                if (GTK_IS_CHECK_MENU_ITEM(menu_cur->data)) {
4889
                        gtk_check_menu_item_set_active
4890
                                (GTK_CHECK_MENU_ITEM(menu_cur->data), FALSE);
4891
                        items[n] = GTK_CHECK_MENU_ITEM(menu_cur->data);
4892
                        n++;
4893
                }
4894
        }
4895
4896
        if (n == (N_COLOR_LABELS + 1)) {
4897
                /* iterate all messages and set the state of the appropriate
4898
                 * items */
4899
                for (cur = mlist; cur != NULL; cur = cur->next) {
4900
                        MsgInfo *msginfo = (MsgInfo *)cur->data;
4901
                        gint clabel;
4902
4903
                        if (msginfo) {
4904
                                clabel = MSG_GET_COLORLABEL_VALUE
4905
                                        (msginfo->flags);
4906
                                if (!gtk_check_menu_item_get_active
4907
                                        (items[clabel]))
4908
                                        gtk_check_menu_item_set_active
4909
                                                (items[clabel], TRUE);
4910
                        }
4911
                }
4912
        } else
4913
                g_warning("invalid number of color elements (%d)\n", n);
4914
4915
        /* reset "dont_toggle" state */
4916
        g_object_set_data(G_OBJECT(menu), "dont_toggle", GINT_TO_POINTER(0));
4917
}
4918
4919
static void summary_colorlabel_menu_create(SummaryView *summaryview)
4920
{
4921
        GtkWidget *label_menuitem;
4922
        GtkWidget *menu;
4923
        GtkWidget *item;
4924
        gint i;
4925
4926
        label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
4927
                                                   "/Color label");
4928
        g_signal_connect(G_OBJECT(label_menuitem), "activate",
4929
                         G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
4930
                         summaryview);
4931
        gtk_widget_show(label_menuitem);
4932
4933
        menu = gtk_menu_new();
4934
4935
        /* create sub items. for the menu item activation callback we pass the
4936
         * color flag value as data parameter. Also we attach a data pointer
4937
         * so we can always get back the SummaryView pointer. */
4938
4939
        item = gtk_check_menu_item_new_with_label(_("None"));
4940
        gtk_menu_append(GTK_MENU(menu), item);
4941
        g_signal_connect(G_OBJECT(item), "activate",
4942
                         G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
4943
                         GUINT_TO_POINTER(0));
4944
        g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
4945
        gtk_widget_show(item);
4946
4947
        item = gtk_menu_item_new();
4948
        gtk_menu_append(GTK_MENU(menu), item);
4949
        gtk_widget_show(item);
4950
4951
        /* create pixmap/label menu items */
4952
        for (i = 0; i < N_COLOR_LABELS; i++) {
4953
                item = colorlabel_create_check_color_menu_item(i);
4954
                gtk_menu_append(GTK_MENU(menu), item);
4955
                g_signal_connect(G_OBJECT(item), "activate",
4956
                                 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
4957
                                 GUINT_TO_POINTER(i + 1));
4958
                g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
4959
                gtk_widget_show(item);
4960
        }
4961
4962
        gtk_widget_show(menu);
4963
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
4964
        summaryview->colorlabel_menu = menu;
4965
}
4966
4967
static GtkWidget *summary_tree_view_create(SummaryView *summaryview)
4968
{
4969
        GtkWidget *treeview;
4970
        GtkTreeStore *store;
4971
        GtkTreeSelection *selection;
4972
        GtkTreeViewColumn *column;
4973
        GtkCellRenderer *renderer;
4974
        GtkWidget *image;
4975
        SummaryColumnType type;
4976
4977
        for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++)
4978
                summaryview->columns[type] = NULL;
4979
4980
        store = gtk_tree_store_new(N_SUMMARY_COLS,
4981
                                   GDK_TYPE_PIXBUF,
4982
                                   GDK_TYPE_PIXBUF,
4983
                                   GDK_TYPE_PIXBUF,
4984
                                   G_TYPE_STRING,
4985
                                   G_TYPE_STRING,
4986
                                   G_TYPE_STRING,
4987
                                   G_TYPE_STRING,
4988
                                   G_TYPE_UINT,
4989
                                   G_TYPE_STRING,
4990
4991
                                   G_TYPE_POINTER,
4992
4993
                                   G_TYPE_INT,
4994
                                   G_TYPE_UINT,
4995
4996
                                   GDK_TYPE_COLOR,
4997
                                   G_TYPE_INT);
4998
4999
#define SET_SORT(col, func)                                                \
5000
        gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store),        \
5001
                                        col, func, summaryview, NULL)
5002
5003
        SET_SORT(S_COL_MARK, summary_cmp_by_mark);
5004
        SET_SORT(S_COL_UNREAD, summary_cmp_by_unread);
5005
        SET_SORT(S_COL_MIME, summary_cmp_by_mime);
5006
        SET_SORT(S_COL_SUBJECT, summary_cmp_by_subject);
5007
        SET_SORT(S_COL_FROM, summary_cmp_by_from);
5008
        SET_SORT(S_COL_DATE, summary_cmp_by_date);
5009
        SET_SORT(S_COL_SIZE, summary_cmp_by_size);
5010
        SET_SORT(S_COL_NUMBER, summary_cmp_by_num);
5011
        SET_SORT(S_COL_TO, summary_cmp_by_to);
5012
        SET_SORT(S_COL_LABEL, summary_cmp_by_label);
5013
        SET_SORT(S_COL_TDATE, summary_cmp_by_thread_date);
5014
5015
#undef SET_SORT
5016
5017
        treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
5018
        gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
5019
        gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview),
5020
                                     prefs_common.enable_rules_hint);
5021
        gtk_tree_view_set_enable_search(GTK_TREE_VIEW(treeview), FALSE);
5022
        gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), S_COL_SUBJECT);
5023
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(treeview), FALSE);
5024
        g_object_set(treeview, "fixed-height-mode", TRUE, NULL);
5025
5026
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
5027
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
5028
        gtk_tree_selection_set_select_function(selection, summary_select_func,
5029
                                               summaryview, NULL);
5030
5031
#define ADD_COLUMN(title, type, col, text_attr, width, align)                \
5032
{                                                                        \
5033
        renderer = gtk_cell_renderer_ ## type ## _new();                \
5034
        g_object_set(renderer, "xalign", align, "ypad", 0, NULL);        \
5035
        column = gtk_tree_view_column_new_with_attributes                \
5036
                (title, renderer, # type , col, NULL);                        \
5037
        g_object_set_data(G_OBJECT(column), "column_id",                \
5038
                          GINT_TO_POINTER(col));                        \
5039
        summaryview->columns[col] = column;                                \
5040
        if (text_attr) {                                                \
5041
                gtk_tree_view_column_set_attributes                        \
5042
                        (column, renderer,                                \
5043
                         # type, col,                                        \
5044
                         "foreground-gdk", S_COL_FOREGROUND,                \
5045
                         "weight", S_COL_BOLD,                                \
5046
                         NULL);                                                \
5047
                gtk_tree_view_column_set_resizable(column, TRUE);        \
5048
        }                                                                \
5049
        gtk_tree_view_column_set_alignment(column, align);                \
5050
        gtk_tree_view_column_set_sizing                                        \
5051
                (column, GTK_TREE_VIEW_COLUMN_FIXED);                        \
5052
        gtk_tree_view_column_set_fixed_width(column, width);                \
5053
        gtk_tree_view_column_set_min_width(column, 8);                        \
5054
        gtk_tree_view_column_set_clickable(column, TRUE);                \
5055
        /* gtk_tree_view_column_set_sort_column_id(column, col); */        \
5056
        gtk_tree_view_column_set_reorderable(column, TRUE);                \
5057
        gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);        \
5058
        g_signal_connect(G_OBJECT(column->button), "clicked",                \
5059
                         G_CALLBACK(summary_column_clicked),                \
5060
                         summaryview);                                        \
5061
        g_signal_connect(G_OBJECT(column->button), "size-allocate",        \
5062
                         G_CALLBACK(summary_col_resized), summaryview);        \
5063
}
5064
5065
        ADD_COLUMN(NULL, pixbuf, S_COL_MARK, FALSE,
5066
                   SUMMARY_COL_MARK_WIDTH, 0.5);
5067
        image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MARK);
5068
        gtk_widget_show(image);
5069
        gtk_tree_view_column_set_widget(column, image);
5070
        ADD_COLUMN(NULL, pixbuf, S_COL_UNREAD, FALSE,
5071
                   SUMMARY_COL_UNREAD_WIDTH, 0.5);
5072
        image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_MAIL_SMALL);
5073
        gtk_widget_show(image);
5074
        gtk_tree_view_column_set_widget(column, image);
5075
        ADD_COLUMN(NULL, pixbuf, S_COL_MIME, FALSE,
5076
                   SUMMARY_COL_MIME_WIDTH, 0.5);
5077
        image = stock_pixbuf_widget(treeview, STOCK_PIXMAP_CLIP);
5078
        gtk_widget_show(image);
5079
        gtk_tree_view_column_set_widget(column, image);
5080
5081
        ADD_COLUMN(_("Subject"), text, S_COL_SUBJECT, TRUE,
5082
                   prefs_common.summary_col_size[S_COL_SUBJECT], 0.0);
5083
        gtk_tree_view_set_expander_column(GTK_TREE_VIEW(treeview), column);
5084
        ADD_COLUMN(_("From"), text, S_COL_FROM, TRUE,
5085
                   prefs_common.summary_col_size[S_COL_FROM], 0.0);
5086
        ADD_COLUMN(_("Date"), text, S_COL_DATE, TRUE,
5087
                   prefs_common.summary_col_size[S_COL_DATE], 0.0);
5088
        ADD_COLUMN(_("Size"), text, S_COL_SIZE, TRUE,
5089
                   prefs_common.summary_col_size[S_COL_SIZE], 1.0);
5090
        ADD_COLUMN(_("No."), text, S_COL_NUMBER, TRUE,
5091
                   prefs_common.summary_col_size[S_COL_NUMBER], 1.0);
5092
        ADD_COLUMN(_("To"), text, S_COL_TO, TRUE,
5093
                   prefs_common.summary_col_size[S_COL_TO], 0.0);
5094
5095
#undef ADD_COLUMN
5096
5097
        g_object_set_data(G_OBJECT(treeview), "user_data", summaryview);
5098
5099
        g_signal_connect(G_OBJECT(treeview), "button_press_event",
5100
                         G_CALLBACK(summary_button_pressed), summaryview);
5101
        g_signal_connect(G_OBJECT(treeview), "button_release_event",
5102
                         G_CALLBACK(summary_button_released), summaryview);
5103
        g_signal_connect(G_OBJECT(treeview), "key_press_event",
5104
                         G_CALLBACK(summary_key_pressed), summaryview);
5105
5106
        g_signal_connect(G_OBJECT(selection), "changed",
5107
                         G_CALLBACK(summary_selection_changed), summaryview);
5108
5109
        g_signal_connect(G_OBJECT(treeview), "row-expanded",
5110
                         G_CALLBACK(summary_row_expanded), summaryview);
5111
        g_signal_connect(G_OBJECT(treeview), "row-collapsed",
5112
                         G_CALLBACK(summary_row_collapsed), summaryview);
5113
5114
        g_signal_connect(G_OBJECT(treeview), "columns-changed",
5115
                         G_CALLBACK(summary_columns_changed), summaryview);
5116
5117
        gtk_tree_view_enable_model_drag_source
5118
                (GTK_TREE_VIEW(treeview),
5119
                 GDK_BUTTON1_MASK, summary_drag_types, N_DRAG_TYPES,
5120
                 GDK_ACTION_MOVE | GDK_ACTION_COPY);
5121
5122
        g_signal_connect_after(G_OBJECT(treeview), "drag-begin",
5123
                         G_CALLBACK(summary_drag_begin), summaryview);
5124
        g_signal_connect_after(G_OBJECT(treeview), "drag-end",
5125
                         G_CALLBACK(summary_drag_end), summaryview);
5126
        g_signal_connect(G_OBJECT(treeview), "drag-data-get",
5127
                         G_CALLBACK(summary_drag_data_get), summaryview);
5128
5129
        return treeview;
5130
}
5131
5132
void summary_set_column_order(SummaryView *summaryview)
5133
{
5134
        const SummaryColumnState *col_state;
5135
        SummaryColumnType type;
5136
        GtkTreeViewColumn *column, *last_column = NULL;
5137
        gint pos;
5138
5139
        g_signal_handlers_block_by_func(summaryview->treeview,
5140
                                        summary_columns_changed, summaryview);
5141
5142
        col_state = prefs_summary_column_get_config
5143
                (FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item));
5144
5145
        for (pos = 0; pos < N_SUMMARY_VISIBLE_COLS; pos++) {
5146
                summaryview->col_state[pos] = col_state[pos];
5147
                type = col_state[pos].type;
5148
                summaryview->col_pos[type] = pos;
5149
                column = summaryview->columns[type];
5150
5151
                gtk_tree_view_move_column_after
5152
                        (GTK_TREE_VIEW(summaryview->treeview),
5153
                         column, last_column);
5154
                gtk_tree_view_column_set_visible
5155
                        (column, col_state[pos].visible);
5156
                last_column = column;
5157
5158
                debug_print("summary_set_column_order: "
5159
                            "pos %d : type %d, vislble %d\n",
5160
                            pos, type, summaryview->col_state[pos].visible);
5161
        }
5162
5163
        g_signal_handlers_unblock_by_func(summaryview->treeview,
5164
                                          summary_columns_changed, summaryview);
5165
}
5166
5167
void summary_get_column_order(SummaryView *summaryview)
5168
{
5169
        GtkTreeViewColumn *column;
5170
        GList *columns, *cur;
5171
        gint pos = 0;
5172
        SummaryColumnType type;
5173
        gboolean visible;
5174
5175
        columns = gtk_tree_view_get_columns
5176
                (GTK_TREE_VIEW(summaryview->treeview));
5177
5178
        for (cur = columns; cur != NULL && pos < N_SUMMARY_VISIBLE_COLS;
5179
             cur = cur->next, pos++) {
5180
                column = (GtkTreeViewColumn *)cur->data;
5181
                type = GPOINTER_TO_INT
5182
                        (g_object_get_data(G_OBJECT(column), "column_id"));
5183
                if (type < 0 || type >= N_SUMMARY_VISIBLE_COLS) {
5184
                        g_warning("summary_get_column_order: "
5185
                                  "invalid type: %d\n", type);
5186
                        break;
5187
                }
5188
                visible = gtk_tree_view_column_get_visible(column);
5189
                summaryview->col_state[pos].type = type;
5190
                summaryview->col_state[pos].visible = visible;
5191
                summaryview->col_pos[type] = pos;
5192
                debug_print("summary_get_column_order: "
5193
                            "pos: %d, type: %d, visible: %d\n",
5194
                            pos, type, visible);
5195
        }
5196
5197
        prefs_summary_column_set_config
5198
                (summaryview->col_state,
5199
                 FOLDER_ITEM_IS_SENT_FOLDER(summaryview->folder_item));
5200
5201
        g_list_free(columns);
5202
}
5203
5204
void summary_qsearch_reset(SummaryView *summaryview)
5205
{
5206
        guint selected_msgnum = 0;
5207
        guint displayed_msgnum = 0;
5208
5209
        if (!summaryview->on_filter)
5210
                return;
5211
5212
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
5213
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5214
                                        0, 0, NULL, NULL, summaryview);
5215
5216
        quick_search_clear_entry(summaryview->qsearch);
5217
        gtk_option_menu_set_history
5218
                (GTK_OPTION_MENU(summaryview->qsearch->optmenu), 0);
5219
        summaryview->folder_item->qsearch_cond_type = QS_ALL;
5220
5221
        selected_msgnum = summary_get_msgnum(summaryview,
5222
                                             summaryview->selected);
5223
        displayed_msgnum = summary_get_msgnum(summaryview,
5224
                                              summaryview->displayed);
5225
5226
        summaryview->on_filter = FALSE;
5227
        g_slist_free(summaryview->flt_mlist);
5228
        summaryview->flt_mlist = NULL;
5229
        summaryview->total_flt_msg_size = 0;
5230
        summaryview->flt_msg_total = 0;
5231
        summaryview->flt_deleted = 0;
5232
        summaryview->flt_moved = 0;
5233
        summaryview->flt_copied = 0;
5234
        summaryview->flt_new = 0;
5235
        summaryview->flt_unread = 0;
5236
5237
        summary_lock(summaryview);
5238
        main_window_cursor_wait(summaryview->mainwin);
5239
5240
        gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview),
5241
                                   summaryview->store);
5242
        summary_unset_sort_column_id(summaryview);
5243
        summaryview->total_size = 0;
5244
        summary_set_tree_model_from_list(summaryview, summaryview->all_mlist);
5245
        summary_selection_list_free(summaryview);
5246
5247
        main_window_cursor_normal(summaryview->mainwin);
5248
        summary_unlock(summaryview);
5249
5250
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
5251
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5252
                                          0, 0, NULL, NULL, summaryview);
5253
5254
        summary_update_display_state(summaryview, displayed_msgnum,
5255
                                     selected_msgnum);
5256
5257
        summary_update_status(summaryview);
5258
        summary_status_show(summaryview);
5259
        summary_set_menu_sensitive(summaryview);
5260
        main_window_set_toolbar_sensitive(summaryview->mainwin);
5261
}
5262
5263
void summary_qsearch_clear_entry(SummaryView *summaryview)
5264
{
5265
        quick_search_clear_entry(summaryview->qsearch);
5266
        summary_qsearch(summaryview);
5267
}
5268
5269
void summary_qsearch(SummaryView *summaryview)
5270
{
5271
        QSearchCondType type;
5272
        GtkWidget *menuitem;
5273
        const gchar *key;
5274
        GSList *flt_mlist;
5275
        guint selected_msgnum = 0;
5276
        guint displayed_msgnum = 0;
5277
5278
        menuitem = gtk_menu_get_active(GTK_MENU(summaryview->qsearch->menu));
5279
        type = GPOINTER_TO_INT
5280
                (g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID));
5281
        summaryview->folder_item->qsearch_cond_type = type;
5282
5283
        if (!summaryview->all_mlist)
5284
                return;
5285
5286
        key = gtk_entry_get_text(GTK_ENTRY(summaryview->qsearch->entry));
5287
        if (type == QS_ALL && (!key || *key == '\0')) {
5288
                summary_qsearch_reset(summaryview);
5289
                return;
5290
        }
5291
5292
        selected_msgnum = summary_get_msgnum(summaryview,
5293
                                             summaryview->selected);
5294
        displayed_msgnum = summary_get_msgnum(summaryview,
5295
                                              summaryview->displayed);
5296
5297
        summaryview->on_filter = FALSE;
5298
        g_slist_free(summaryview->flt_mlist);
5299
        summaryview->flt_mlist = NULL;
5300
        summaryview->total_flt_msg_size = 0;
5301
        summaryview->flt_msg_total = 0;
5302
        summaryview->flt_deleted = 0;
5303
        summaryview->flt_moved = 0;
5304
        summaryview->flt_copied = 0;
5305
        summaryview->flt_new = 0;
5306
        summaryview->flt_unread = 0;
5307
5308
        summary_lock(summaryview);
5309
        main_window_cursor_wait(summaryview->mainwin);
5310
5311
        flt_mlist = quick_search_filter(summaryview->qsearch, type, key);
5312
        summaryview->on_filter = TRUE;
5313
        summaryview->flt_mlist = flt_mlist;
5314
5315
        g_signal_handlers_block_matched(G_OBJECT(summaryview->treeview),
5316
                                        (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5317
                                        0, 0, NULL, NULL, summaryview);
5318
5319
        gtkut_tree_view_fast_clear(GTK_TREE_VIEW(summaryview->treeview),
5320
                                   summaryview->store);
5321
        summary_unset_sort_column_id(summaryview);
5322
        summaryview->total_size = 0;
5323
        summary_set_tree_model_from_list(summaryview, flt_mlist);
5324
        summary_selection_list_free(summaryview);
5325
5326
        g_signal_handlers_unblock_matched(G_OBJECT(summaryview->treeview),
5327
                                          (GSignalMatchType)G_SIGNAL_MATCH_DATA,
5328
                                          0, 0, NULL, NULL, summaryview);
5329
5330
        summary_update_display_state(summaryview, displayed_msgnum,
5331
                                     selected_msgnum);
5332
5333
        main_window_cursor_normal(summaryview->mainwin);
5334
        summary_unlock(summaryview);
5335
        summary_update_status(summaryview);
5336
        summary_status_show(summaryview);
5337
        summary_set_menu_sensitive(summaryview);
5338
        main_window_set_toolbar_sensitive(summaryview->mainwin);
5339
}
5340
5341
5342
/* callback functions */
5343
5344
static gboolean summary_toggle_pressed(GtkWidget *eventbox,
5345
                                       GdkEventButton *event,
5346
                                       SummaryView *summaryview)
5347
{
5348
        if (!event) return FALSE;
5349
5350
        summary_toggle_view(summaryview);
5351
        return FALSE;
5352
}
5353
5354
static gboolean summary_button_pressed(GtkWidget *treeview,
5355
                                       GdkEventButton *event,
5356
                                       SummaryView *summaryview)
5357
{
5358
        GtkTreeIter iter;
5359
        GtkTreePath *path;
5360
        GtkTreeViewColumn *column = NULL;
5361
        gboolean is_selected;
5362
        gboolean mod_pressed;
5363
        gint px, py;
5364
5365
        if (!event) return FALSE;
5366
5367
        if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
5368
                                           event->x, event->y, &path, &column,
5369
                                           NULL, NULL))
5370
                return FALSE;
5371
5372
        /* pass through if the border of column titles is clicked */
5373
        gtk_widget_get_pointer(treeview, &px, &py);
5374
        if (py == (gint)event->y)
5375
                return FALSE;
5376
5377
        if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store),
5378
                                     &iter, path))
5379
                return FALSE;
5380
5381
        is_selected = gtk_tree_selection_path_is_selected
5382
                (summaryview->selection, path);
5383
        mod_pressed = ((event->state &
5384
                        (GDK_SHIFT_MASK|GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0);
5385
5386
        if ((event->button == 1 || event->button == 2)) {
5387
                MsgInfo *msginfo;
5388
                FolderSortKey sort_key = SORT_BY_NONE;
5389
                FolderSortType sort_type = SORT_ASCENDING;
5390
5391
                GET_MSG_INFO(msginfo, &iter);
5392
5393
                if (column == summaryview->columns[S_COL_MARK]) {
5394
                        SORT_BLOCK(SORT_BY_MARK);
5395
5396
                        if (!MSG_IS_DELETED(msginfo->flags) &&
5397
                            !MSG_IS_MOVE(msginfo->flags) &&
5398
                            !MSG_IS_COPY(msginfo->flags)) {
5399
                                if (MSG_IS_MARKED(msginfo->flags)) {
5400
                                        summary_unmark_row(summaryview, &iter);
5401
                                        if (MSG_IS_IMAP(msginfo->flags))
5402
                                                imap_msg_unset_perm_flags
5403
                                                        (msginfo, MSG_MARKED);
5404
                                } else {
5405
                                        summary_mark_row(summaryview, &iter);
5406
                                        if (MSG_IS_IMAP(msginfo->flags))
5407
                                                imap_msg_set_perm_flags
5408
                                                        (msginfo, MSG_MARKED);
5409
                                }
5410
                        }
5411
                        gtk_tree_path_free(path);
5412
5413
                        SORT_UNBLOCK(SORT_BY_MARK);
5414
5415
                        return TRUE;
5416
                } else if (column == summaryview->columns[S_COL_UNREAD]) {
5417
                        SORT_BLOCK(SORT_BY_UNREAD);
5418
5419
                        if (MSG_IS_UNREAD(msginfo->flags)) {
5420
                                summary_mark_row_as_read(summaryview, &iter);
5421
                                if (MSG_IS_IMAP(msginfo->flags))
5422
                                        imap_msg_unset_perm_flags
5423
                                                (msginfo, MSG_NEW | MSG_UNREAD);
5424
                                trayicon_set_tooltip(NULL);
5425
                                trayicon_set_notify(FALSE);
5426
                                summary_status_show(summaryview);
5427
                        } else if (!MSG_IS_REPLIED(msginfo->flags) &&
5428
                                   !MSG_IS_FORWARDED(msginfo->flags)) {
5429
                                summary_mark_row_as_unread(summaryview, &iter);
5430
                                if (MSG_IS_IMAP(msginfo->flags))
5431
                                        imap_msg_set_perm_flags
5432
                                                (msginfo, MSG_UNREAD);
5433
                                summary_status_show(summaryview);
5434
                        }
5435
                        gtk_tree_path_free(path);
5436
5437
                        SORT_UNBLOCK(SORT_BY_UNREAD);
5438
5439
                        return TRUE;
5440
                }
5441
        }
5442
5443
        if (event->button == 1) {
5444
                summaryview->on_button_press = TRUE;
5445
5446
                if (summary_get_selection_type(summaryview) ==
5447
                        SUMMARY_SELECTED_MULTIPLE && is_selected &&
5448
                        !mod_pressed) {
5449
                        summaryview->can_toggle_selection = FALSE;
5450
                        summaryview->pressed_path = gtk_tree_path_copy(path);
5451
                } else {
5452
                        if (event->type == GDK_2BUTTON_PRESS && is_selected)
5453
                                summary_activate_selected(summaryview);
5454
                        else {
5455
                                summaryview->can_toggle_selection = TRUE;
5456
                                if (!mod_pressed &&
5457
                                    messageview_is_visible(summaryview->messageview))
5458
                                        summaryview->display_msg = TRUE;
5459
                        }
5460
                }
5461
        } else if (event->button == 2) {
5462
                summary_select_row(summaryview, &iter, TRUE, FALSE);
5463
                gtk_tree_path_free(path);
5464
                return TRUE;
5465
        } else if (event->button == 3) {
5466
                /* right clicked */
5467
                gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
5468
                               NULL, NULL, event->button, event->time);
5469
                if (is_selected) {
5470
                        gtk_tree_path_free(path);
5471
                        return TRUE;
5472
                }
5473
        }
5474
5475
        gtk_tree_path_free(path);
5476
5477
        return FALSE;
5478
}
5479
5480
static gboolean summary_button_released(GtkWidget *treeview,
5481
                                        GdkEventButton *event,
5482
                                        SummaryView *summaryview)
5483
{
5484
        if (!summaryview->can_toggle_selection && !summaryview->on_drag &&
5485
            summaryview->pressed_path) {
5486
                summaryview->can_toggle_selection = TRUE;
5487
                summaryview->display_msg =
5488
                        messageview_is_visible(summaryview->messageview);
5489
                gtk_tree_view_set_cursor(GTK_TREE_VIEW(treeview),
5490
                                         summaryview->pressed_path,
5491
                                         NULL, FALSE);
5492
        }
5493
5494
        summaryview->on_button_press = FALSE;
5495
        summaryview->can_toggle_selection = TRUE;
5496
        summaryview->on_drag = FALSE;
5497
        if (summaryview->pressed_path) {
5498
                gtk_tree_path_free(summaryview->pressed_path);
5499
                summaryview->pressed_path = NULL;
5500
        }
5501
5502
        return FALSE;
5503
}
5504
5505
void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
5506
{
5507
        summary_key_pressed(summaryview->treeview, event, summaryview);
5508
}
5509
5510
#define BREAK_ON_MODIFIER_KEY() \
5511
        if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
5512
5513
static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
5514
                                    SummaryView *summaryview)
5515
{
5516
        MessageView *messageview;
5517
        TextView *textview;
5518
        GtkAdjustment *adj;
5519
        gboolean mod_pressed;
5520
5521
        if (summary_is_locked(summaryview)) return FALSE;
5522
        if (!event) return FALSE;
5523
5524
        switch (event->keyval) {
5525
        case GDK_Left:                /* Move focus */
5526
        case GDK_KP_Left:
5527
                adj = gtk_scrolled_window_get_hadjustment
5528
                        (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
5529
                if (adj->lower != adj->value)
5530
                        return FALSE;
5531
                /* FALLTHROUGH */
5532
        case GDK_Escape:
5533
                gtk_widget_grab_focus(summaryview->folderview->treeview);
5534
                return TRUE;
5535
        default:
5536
                break;
5537
        }
5538
5539
        if (!summaryview->selected)
5540
                return FALSE;
5541
5542
        messageview = summaryview->messageview;
5543
        if (messageview->type == MVIEW_MIME &&
5544
            gtk_notebook_get_current_page
5545
                (GTK_NOTEBOOK(messageview->notebook)) == 1)
5546
                textview = messageview->mimeview->textview;
5547
        else
5548
                textview = messageview->textview;
5549
5550
        mod_pressed =
5551
                ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
5552
5553
        switch (event->keyval) {
5554
        case GDK_space:                /* Page down or go to the next */
5555
        case GDK_KP_Space:
5556
                if (summaryview->selected &&
5557
                    !gtkut_tree_row_reference_equal(summaryview->displayed,
5558
                                                    summaryview->selected)) {
5559
                        summary_display_msg_selected(summaryview, FALSE, FALSE);
5560
                } else if (mod_pressed) {
5561
                        if (!textview_scroll_page(textview, TRUE))
5562
                                summary_select_prev_unread(summaryview);
5563
                } else {
5564
                        if (!textview_scroll_page(textview, FALSE))
5565
                                summary_select_next_unread(summaryview);
5566
                }
5567
                return TRUE;
5568
        case GDK_BackSpace:        /* Page up */
5569
                textview_scroll_page(textview, TRUE);
5570
                return TRUE;
5571
        case GDK_Return:        /* Scroll up/down one line */
5572
        case GDK_KP_Enter:
5573
                if (summaryview->selected &&
5574
                    !gtkut_tree_row_reference_equal(summaryview->displayed,
5575
                                                    summaryview->selected)) {
5576
                        summary_display_msg_selected(summaryview, FALSE, FALSE);
5577
                } else
5578
                        textview_scroll_one_line(textview, mod_pressed);
5579
                return TRUE;
5580
        case GDK_Delete:
5581
        case GDK_KP_Delete:
5582
                BREAK_ON_MODIFIER_KEY();
5583
                summary_delete(summaryview);
5584
                return TRUE;
5585
        default:
5586
                break;
5587
        }
5588
5589
        return FALSE;
5590
}
5591
5592
static void summary_set_bold_recursive(SummaryView *summaryview,
5593
                                       GtkTreeIter *iter)
5594
{
5595
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
5596
        GtkTreeIter child;
5597
        MsgInfo *msginfo;
5598
        gboolean valid;
5599
5600
        if (!gtk_tree_model_iter_has_child(model, iter))
5601
                return;
5602
5603
        GET_MSG_INFO(msginfo, iter);
5604
        if (!MSG_IS_UNREAD(msginfo->flags)) {
5605
                gtk_tree_store_set(summaryview->store, iter,
5606
                                   S_COL_BOLD, PANGO_WEIGHT_NORMAL, -1);
5607
        }
5608
5609
        valid = gtk_tree_model_iter_children(model, &child, iter);
5610
        while (valid) {
5611
                summary_set_bold_recursive(summaryview, &child);
5612
                valid = gtk_tree_model_iter_next(model, &child);
5613
        }
5614
}
5615
5616
static void summary_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter,
5617
                                 GtkTreePath *path, SummaryView *summaryview)
5618
{
5619
        gtk_tree_view_expand_row(treeview, path, TRUE);
5620
5621
        if (prefs_common.bold_unread)
5622
                summary_set_bold_recursive(summaryview, iter);
5623
5624
        /* workaround for last row expand problem */
5625
#if GTK_CHECK_VERSION(2, 8, 0)
5626
        gtk_widget_queue_resize(GTK_WIDGET(treeview));
5627
#else
5628
        g_object_set(treeview, "fixed-height-mode", FALSE, NULL);
5629
        gtk_widget_queue_resize(GTK_WIDGET(treeview));
5630
        g_object_set(treeview, "fixed-height-mode", TRUE, NULL);
5631
#endif
5632
}
5633
5634
static void summary_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter,
5635
                                  GtkTreePath *path, SummaryView *summaryview)
5636
{
5637
        if (prefs_common.bold_unread &&
5638
            summary_have_unread_children(summaryview, iter)) {
5639
                gtk_tree_store_set(summaryview->store, iter,
5640
                                   S_COL_BOLD, PANGO_WEIGHT_BOLD, -1);
5641
        }
5642
}
5643
5644
static void summary_columns_changed(GtkTreeView *treeview,
5645
                                    SummaryView *summaryview)
5646
{
5647
        summary_get_column_order(summaryview);
5648
}
5649
5650
static gboolean summary_select_func(GtkTreeSelection *treeview,
5651
                                    GtkTreeModel *model, GtkTreePath *path,
5652
                                    gboolean cur_selected, gpointer data)
5653
{
5654
        SummaryView *summaryview = (SummaryView *)data;
5655
5656
        return summaryview->can_toggle_selection;
5657
}
5658
5659
static gboolean summary_display_msg_idle_func(gpointer data)
5660
{
5661
        SummaryView *summaryview = (SummaryView *)data;
5662
        GtkTreePath *path;
5663
        GtkTreeIter iter;
5664
5665
        path = gtk_tree_row_reference_get_path(summaryview->selected);
5666
        if (path) {
5667
                gtk_tree_model_get_iter(GTK_TREE_MODEL(summaryview->store),
5668
                                        &iter, path);
5669
                g_free(path);
5670
                summary_display_msg(summaryview, &iter);
5671
        }
5672
5673
        return FALSE;
5674
}
5675
5676
static void summary_selection_changed(GtkTreeSelection *selection,
5677
                                      SummaryView *summaryview)
5678
{
5679
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
5680
        GtkTreeIter iter;
5681
        GtkTreePath *path;
5682
        GList *list;
5683
        gboolean single_selection = FALSE;
5684
5685
        summary_selection_list_free(summaryview);
5686
        list = summary_get_selected_rows(summaryview);
5687
5688
        summary_status_show(summaryview);
5689
5690
        gtk_tree_row_reference_free(summaryview->selected);
5691
        if (list) {
5692
                if (list->next == NULL)
5693
                        single_selection = TRUE;
5694
                path = (GtkTreePath *)list->data;
5695
                gtk_tree_model_get_iter(model, &iter, path);
5696
                summaryview->selected =
5697
                        gtk_tree_row_reference_new(model, path);
5698
        } else
5699
                summaryview->selected = NULL;
5700
5701
        if (!single_selection) {
5702
                summaryview->display_msg = FALSE;
5703
                if (summaryview->displayed && prefs_common.always_show_msg) {
5704
                        messageview_clear(summaryview->messageview);
5705
                        gtk_tree_row_reference_free(summaryview->displayed);
5706
                        summaryview->displayed = NULL;
5707
                }
5708
                summary_set_menu_sensitive(summaryview);
5709
                main_window_set_toolbar_sensitive(summaryview->mainwin);
5710
                return;
5711
        }
5712
5713
        if (summaryview->display_msg ||
5714
            (prefs_common.always_show_msg &&
5715
             messageview_is_visible(summaryview->messageview))) {
5716
                summaryview->display_msg = FALSE;
5717
                if (!gtkut_tree_row_reference_equal(summaryview->displayed,
5718
                                                    summaryview->selected)) {
5719
                        if (summaryview->on_button_press) {
5720
                                MsgInfo *msginfo;
5721
5722
                                gtk_tree_model_get(model, &iter,
5723
                                                   S_COL_MSG_INFO, &msginfo,
5724
                                                   -1);
5725
                                if (MSG_IS_MIME(msginfo->flags))
5726
                                        g_idle_add
5727
                                                (summary_display_msg_idle_func,
5728
                                                 summaryview);
5729
                                else
5730
                                        summary_display_msg(summaryview, &iter);
5731
                        } else
5732
                                summary_display_msg(summaryview, &iter);
5733
                        return;
5734
                }
5735
        }
5736
5737
        summary_set_menu_sensitive(summaryview);
5738
        main_window_set_toolbar_sensitive(summaryview->mainwin);
5739
}
5740
5741
static void summary_col_resized(GtkWidget *widget, GtkAllocation *allocation,
5742
                                SummaryView *summaryview)
5743
{
5744
        SummaryColumnType type;
5745
5746
        for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) {
5747
                if (summaryview->columns[type]->button == widget) {
5748
                        prefs_common.summary_col_size[type] = allocation->width;
5749
                        break;
5750
                }
5751
        }
5752
}
5753
5754
static void summary_reply_cb(SummaryView *summaryview, guint action,
5755
                             GtkWidget *widget)
5756
{
5757
        summary_reply(summaryview, (ComposeMode)action);
5758
}
5759
5760
static void summary_show_all_header_cb(SummaryView *summaryview,
5761
                                       guint action, GtkWidget *widget)
5762
{
5763
        summary_display_msg_selected(summaryview, FALSE,
5764
                                     GTK_CHECK_MENU_ITEM(widget)->active);
5765
}
5766
5767
static void summary_add_address_cb(SummaryView *summaryview,
5768
                                   guint action, GtkWidget *widget)
5769
{
5770
        summary_add_address(summaryview);
5771
}
5772
5773
static void summary_create_filter_cb(SummaryView *summaryview,
5774
                                     guint action, GtkWidget *widget)
5775
{
5776
        summary_filter_open(summaryview, (FilterCreateType)action);
5777
}
5778
5779
static void summary_sort_by_column_click(SummaryView *summaryview,
5780
                                         SummaryColumnType type)
5781
{
5782
        FolderItem *item = summaryview->folder_item;
5783
5784
        if (!item) return;
5785
5786
        if (item->sort_key == col_to_sort_key[type])
5787
                summary_sort(summaryview, col_to_sort_key[type],
5788
                             item->sort_type == SORT_ASCENDING
5789
                             ? SORT_DESCENDING : SORT_ASCENDING);
5790
        else
5791
                summary_sort(summaryview, col_to_sort_key[type],
5792
                             SORT_ASCENDING);
5793
}
5794
5795
static void summary_column_clicked(GtkWidget *button, SummaryView *summaryview)
5796
{
5797
        SummaryColumnType type;
5798
5799
        for (type = 0; type < N_SUMMARY_VISIBLE_COLS; type++) {
5800
                if (summaryview->columns[type]->button == button) {
5801
                        summary_sort_by_column_click(summaryview, type);
5802
                        break;
5803
                }
5804
        }
5805
}
5806
5807
static void summary_drag_begin(GtkWidget *widget, GdkDragContext *drag_context,
5808
                               SummaryView *summaryview)
5809
{
5810
        summaryview->on_drag = TRUE;
5811
        gtk_drag_set_icon_default(drag_context);
5812
}
5813
5814
static void summary_drag_end(GtkWidget *widget, GdkDragContext *drag_context,
5815
                             SummaryView *summaryview)
5816
{
5817
        if (summaryview->drag_list) {
5818
                g_free(summaryview->drag_list);
5819
                summaryview->drag_list = NULL;
5820
        }
5821
}
5822
5823
static void summary_drag_data_get(GtkWidget        *widget,
5824
                                  GdkDragContext   *drag_context,
5825
                                  GtkSelectionData *selection_data,
5826
                                  guint             info,
5827
                                  guint             time,
5828
                                  SummaryView      *summaryview)
5829
{
5830
        GtkTreeModel *model = GTK_TREE_MODEL(summaryview->store);
5831
        GList *rows, *cur;
5832
        gchar *mail_list = NULL;
5833
        gchar *file, *filename, *fs_filename, *tmp;
5834
        gint suffix = 0;
5835
        MsgInfo *msginfo;
5836
        GtkTreeIter iter;
5837
5838
        if (info == DRAG_TYPE_TEXT) {
5839
                gtk_selection_data_set(selection_data, selection_data->target,
5840
                                       8, (guchar *)"drag-from-summary", 17);
5841
                return;
5842
        }
5843
5844
        if (!summaryview->drag_list) {
5845
                rows = summary_get_selected_rows(summaryview);
5846
5847
                for (cur = rows; cur != NULL; cur = cur->next) {
5848
                        gtk_tree_model_get_iter(model, &iter,
5849
                                                (GtkTreePath *)cur->data);
5850
                        gtk_tree_model_get(model, &iter, S_COL_MSG_INFO,
5851
                                           &msginfo, -1);
5852
                        file = procmsg_get_message_file(msginfo);
5853
                        if (!file) continue;
5854
5855
                        if (msginfo->subject && *msginfo->subject != '\0') {
5856
                                filename = g_strdup(msginfo->subject);
5857
                                subst_for_filename(filename);
5858
                        } else
5859
                                filename = g_strdup(g_basename(file));
5860
                        fs_filename = conv_filename_from_utf8(filename);
5861
5862
                        suffix = 0;
5863
                        do {
5864
                                if (suffix == 0)
5865
                                        tmp = g_strdup_printf
5866
                                                ("%s%c%s.eml", get_tmp_dir(),
5867
                                                 G_DIR_SEPARATOR,
5868
                                                 fs_filename);
5869
                                else
5870
                                        tmp = g_strdup_printf
5871
                                                ("%s%c%s_(%d).eml",
5872
                                                 get_tmp_dir(),
5873
                                                 G_DIR_SEPARATOR, fs_filename,
5874
                                                 suffix);
5875
5876
                                if (is_file_exist(tmp)) {
5877
                                        suffix++;
5878
                                        g_free(tmp);
5879
                                } else
5880
                                        break;
5881
                        } while (1);
5882
5883
                        if (copy_file(file, tmp, FALSE) < 0) {
5884
                                g_warning("Can't copy '%s'\n", file);
5885
                        } else {
5886
                                gchar *uri;
5887
5888
                                uri = encode_uri(tmp);
5889
5890
                                if (!mail_list) {
5891
                                        mail_list = uri;
5892
                                } else {
5893
                                        gchar *list_tmp;
5894
5895
                                        list_tmp = g_strconcat
5896
                                                (mail_list, "\n", uri, NULL);
5897
                                        g_free(mail_list);
5898
                                        g_free(uri);
5899
                                        mail_list = list_tmp;
5900
                                }
5901
                        }
5902
5903
                        g_free(tmp);
5904
                        g_free(fs_filename);
5905
                        g_free(filename);
5906
                        g_free(file);
5907
                }
5908
5909
                summaryview->drag_list = mail_list;
5910
        }
5911
5912
        if (summaryview->drag_list) {
5913
                gtk_selection_data_set(selection_data,
5914
                                       selection_data->target, 8,
5915
                                       (guchar *)summaryview->drag_list,
5916
                                       strlen(summaryview->drag_list));
5917
        }
5918
}
5919
5920
5921
/* custom compare functions for sorting */
5922
5923
#define CMP_FUNC_DEF(func_name, val)                                        \
5924
static gint func_name(GtkTreeModel *model,                                \
5925
                      GtkTreeIter *a, GtkTreeIter *b, gpointer data)        \
5926
{                                                                        \
5927
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;                        \
5928
        gint ret;                                                        \
5929
                                                                        \
5930
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1);        \
5931
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1);        \
5932
                                                                        \
5933
        if (!msginfo_a || !msginfo_b)                                        \
5934
                return 0;                                                \
5935
                                                                        \
5936
        ret = (val);                                                        \
5937
        return (ret != 0) ? ret :                                        \
5938
                (msginfo_a->date_t - msginfo_b->date_t);                \
5939
}
5940
5941
CMP_FUNC_DEF(summary_cmp_by_mark,
5942
             MSG_IS_MARKED(msginfo_a->flags) - MSG_IS_MARKED(msginfo_b->flags))
5943
CMP_FUNC_DEF(summary_cmp_by_unread,
5944
             MSG_IS_UNREAD(msginfo_a->flags) - MSG_IS_UNREAD(msginfo_b->flags))
5945
CMP_FUNC_DEF(summary_cmp_by_mime,
5946
             MSG_IS_MIME(msginfo_a->flags) - MSG_IS_MIME(msginfo_b->flags))
5947
CMP_FUNC_DEF(summary_cmp_by_label,
5948
             MSG_GET_COLORLABEL(msginfo_a->flags) -
5949
             MSG_GET_COLORLABEL(msginfo_b->flags))
5950
CMP_FUNC_DEF(summary_cmp_by_size, msginfo_a->size - msginfo_b->size)
5951
5952
#undef CMP_FUNC_DEF
5953
#define CMP_FUNC_DEF(func_name, val)                                        \
5954
static gint func_name(GtkTreeModel *model,                                \
5955
                      GtkTreeIter *a, GtkTreeIter *b, gpointer data)        \
5956
{                                                                        \
5957
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;                        \
5958
                                                                        \
5959
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1);        \
5960
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1);        \
5961
                                                                        \
5962
        if (!msginfo_a || !msginfo_b)                                        \
5963
                return 0;                                                \
5964
                                                                        \
5965
        return (val);                                                        \
5966
}
5967
5968
CMP_FUNC_DEF(summary_cmp_by_num, msginfo_a->msgnum - msginfo_b->msgnum)
5969
CMP_FUNC_DEF(summary_cmp_by_date, msginfo_a->date_t - msginfo_b->date_t)
5970
5971
#undef CMP_FUNC_DEF
5972
5973
static gint summary_cmp_by_thread_date(GtkTreeModel *model,
5974
                                       GtkTreeIter *a, GtkTreeIter *b,
5975
                                       gpointer data)
5976
{
5977
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
5978
        guint tdate_a, tdate_b;
5979
5980
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, S_COL_TDATE,
5981
                           &tdate_a, -1);
5982
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, S_COL_TDATE,
5983
                           &tdate_b, -1);
5984
5985
        if (!msginfo_a || !msginfo_b)
5986
                return 0;
5987
5988
        if (tdate_a == 0 && tdate_b == 0)
5989
                return msginfo_a->date_t - msginfo_b->date_t;
5990
        else
5991
                return tdate_a - tdate_b;
5992
}
5993
5994
#define CMP_FUNC_DEF(func_name, var_name)                                \
5995
static gint func_name(GtkTreeModel *model,                                \
5996
                      GtkTreeIter *a, GtkTreeIter *b, gpointer data)        \
5997
{                                                                        \
5998
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;                        \
5999
        gint ret;                                                        \
6000
                                                                        \
6001
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1);        \
6002
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1);        \
6003
                                                                        \
6004
        if (!msginfo_a || !msginfo_b)                                        \
6005
                return 0;                                                \
6006
                                                                        \
6007
        if (msginfo_a->var_name == NULL)                                \
6008
                return -(msginfo_b->var_name != NULL);                        \
6009
        if (msginfo_b->var_name == NULL)                                \
6010
                return (msginfo_a->var_name != NULL);                        \
6011
                                                                        \
6012
        ret = g_ascii_strcasecmp                                        \
6013
                (msginfo_a->var_name, msginfo_b->var_name);                \
6014
                                                                        \
6015
        return (ret != 0) ? ret :                                        \
6016
                (msginfo_a->date_t - msginfo_b->date_t);                \
6017
}
6018
6019
CMP_FUNC_DEF(summary_cmp_by_from, fromname)
6020
6021
#undef CMP_FUNC_DEF
6022
6023
static gint summary_cmp_by_to(GtkTreeModel *model,
6024
                                   GtkTreeIter *a, GtkTreeIter *b,
6025
                                   gpointer data)
6026
{
6027
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
6028
        gchar *to_a = NULL, *to_b = NULL;
6029
        gint ret;
6030
6031
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a,
6032
                           S_COL_TO, &to_a, -1);
6033
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b,
6034
                           S_COL_TO, &to_b, -1);
6035
6036
        if (!msginfo_a || !msginfo_b) {
6037
                g_free(to_b);
6038
                g_free(to_a);
6039
                return 0;
6040
        }
6041
6042
        ret = g_ascii_strcasecmp(to_a ? to_a : "", to_b ? to_b : "");
6043
6044
        g_free(to_b);
6045
        g_free(to_a);
6046
6047
        return (ret != 0) ? ret :
6048
                (msginfo_a->date_t - msginfo_b->date_t);
6049
}
6050
6051
static gint summary_cmp_by_subject(GtkTreeModel *model,
6052
                                   GtkTreeIter *a, GtkTreeIter *b,
6053
                                   gpointer data)
6054
{
6055
        MsgInfo *msginfo_a = NULL, *msginfo_b = NULL;
6056
        gint ret;
6057
6058
        gtk_tree_model_get(model, a, S_COL_MSG_INFO, &msginfo_a, -1);
6059
        gtk_tree_model_get(model, b, S_COL_MSG_INFO, &msginfo_b, -1);
6060
6061
        if (!msginfo_a || !msginfo_b)
6062
                return 0;
6063
6064
        if (msginfo_a->subject == NULL)
6065
                return -(msginfo_b->subject != NULL);
6066
        if (msginfo_b->subject == NULL)
6067
                return (msginfo_a->subject != NULL);
6068
6069
        ret = subject_compare_for_sort(msginfo_a->subject, msginfo_b->subject);
6070
6071
        return (ret != 0) ? ret :
6072
                (msginfo_a->date_t - msginfo_b->date_t);
6073
}