Statistics
| Revision:

root / src / summaryview.c @ 3159

History | View | Annotate | Download (181.7 kB)

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