Statistics
| Revision:

root / src / summaryview.c @ 1689

History | View | Annotate | Download (169.7 kB)

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