Statistics
| Revision:

root / src / summaryview.c @ 30

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