Statistics
| Revision:

root / src / summaryview.c @ 257

History | View | Annotate | Download (119.7 kB)

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