Statistics
| Revision:

root / src / summaryview.c @ 944

History | View | Annotate | Download (161.7 kB)

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