Statistics
| Revision:

root / src / summaryview.c @ 92

History | View | Annotate | Download (117.3 kB)

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