Statistics
| Revision:

root / src / summaryview.c @ 416

History | View | Annotate | Download (133 kB)

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