Statistics
| Revision:

root / src / summaryview.c @ 881

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