2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto
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.
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.
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.
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtkscrolledwindow.h>
25 #include <gtk/gtkwidget.h>
26 #include <gtk/gtkpixmap.h>
27 #include <gtk/gtkctree.h>
28 #include <gtk/gtkcontainer.h>
29 #include <gtk/gtksignal.h>
30 #include <gtk/gtktext.h>
31 #include <gtk/gtkmenu.h>
32 #include <gtk/gtkmenuitem.h>
33 #include <gtk/gtkitemfactory.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkwindow.h>
37 #include <gtk/gtkstyle.h>
38 #include <gtk/gtkarrow.h>
39 #include <gtk/gtkeventbox.h>
40 #include <gtk/gtkstatusbar.h>
41 #include <gtk/gtkmenuitem.h>
53 #include "mainwindow.h"
54 #include "folderview.h"
55 #include "summaryview.h"
56 #include "messageview.h"
57 #include "foldersel.h"
59 #include "procheader.h"
60 #include "headerwindow.h"
61 #include "sourcewindow.h"
62 #include "prefs_common.h"
68 #include "manage_window.h"
69 #include "alertpanel.h"
70 #include "inputdialog.h"
71 #include "statusbar.h"
74 #include "colorlabel.h"
76 #include "addressbook.h"
77 #include "addr_compl.h"
79 #include "prefs_folder_item.h"
80 #include "filtering.h"
82 #include "pixmaps/dir-open.xpm"
83 #include "pixmaps/mark.xpm"
84 #include "pixmaps/deleted.xpm"
85 #include "pixmaps/new.xpm"
86 #include "pixmaps/unread.xpm"
87 #include "pixmaps/replied.xpm"
88 #include "pixmaps/forwarded.xpm"
89 #include "pixmaps/clip.xpm"
90 #include "pixmaps/ignorethread.xpm"
92 #define STATUSBAR_PUSH(mainwin, str) \
94 gtk_statusbar_push(GTK_STATUSBAR(mainwin->statusbar), \
95 mainwin->summaryview_cid, str); \
96 gtkut_widget_wait_for_draw(mainwin->hbox_stat); \
99 #define STATUSBAR_POP(mainwin) \
101 gtk_statusbar_pop(GTK_STATUSBAR(mainwin->statusbar), \
102 mainwin->summaryview_cid); \
105 #define SUMMARY_COL_MARK_WIDTH 10
106 #define SUMMARY_COL_UNREAD_WIDTH 13
107 #define SUMMARY_COL_MIME_WIDTH 10
109 static GdkFont *boldfont;
110 static GdkFont *smallfont;
112 static GtkStyle *bold_style;
113 static GtkStyle *bold_marked_style;
114 static GtkStyle *bold_deleted_style;
115 static GtkStyle *small_style;
116 static GtkStyle *small_marked_style;
117 static GtkStyle *small_deleted_style;
119 static GdkPixmap *folderxpm;
120 static GdkBitmap *folderxpmmask;
122 static GdkPixmap *markxpm;
123 static GdkBitmap *markxpmmask;
124 static GdkPixmap *deletedxpm;
125 static GdkBitmap *deletedxpmmask;
127 static GdkPixmap *newxpm;
128 static GdkBitmap *newxpmmask;
129 static GdkPixmap *unreadxpm;
130 static GdkBitmap *unreadxpmmask;
131 static GdkPixmap *repliedxpm;
132 static GdkBitmap *repliedxpmmask;
133 static GdkPixmap *forwardedxpm;
134 static GdkBitmap *forwardedxpmmask;
135 static GdkPixmap *ignorethreadxpm;
136 static GdkBitmap *ignorethreadxpmmask;
138 static GdkPixmap *clipxpm;
139 static GdkBitmap *clipxpmmask;
141 static void summary_free_msginfo_func (GtkCTree *ctree,
144 static void summary_set_marks_func (GtkCTree *ctree,
147 static void summary_write_cache_func (GtkCTree *ctree,
151 static void summary_set_menu_sensitive (SummaryView *summaryview);
153 static guint summary_get_msgnum (SummaryView *summaryview,
156 static GtkCTreeNode *summary_find_prev_msg
157 (SummaryView *summaryview,
158 GtkCTreeNode *current_node);
159 static GtkCTreeNode *summary_find_next_msg
160 (SummaryView *summaryview,
161 GtkCTreeNode *current_node);
162 static GtkCTreeNode *summary_find_prev_unread_msg
163 (SummaryView *summaryview,
164 GtkCTreeNode *current_node);
165 static GtkCTreeNode *summary_find_next_unread_msg
166 (SummaryView *summaryview,
167 GtkCTreeNode *current_node);
168 static GtkCTreeNode *summary_find_prev_marked_msg
169 (SummaryView *summaryview,
170 GtkCTreeNode *current_node);
171 static GtkCTreeNode *summary_find_next_marked_msg
172 (SummaryView *summaryview,
173 GtkCTreeNode *current_node);
174 static GtkCTreeNode *summary_find_prev_labeled_msg
175 (SummaryView *summaryview,
176 GtkCTreeNode *current_node);
177 static GtkCTreeNode *summary_find_next_labeled_msg
178 (SummaryView *summaryview,
179 GtkCTreeNode *current_node);
180 static GtkCTreeNode *summary_find_msg_by_msgnum
181 (SummaryView *summaryview,
184 static void summary_update_status (SummaryView *summaryview);
186 /* display functions */
187 static void summary_status_show (SummaryView *summaryview);
188 static void summary_set_ctree_from_list (SummaryView *summaryview,
190 static void summary_set_header (SummaryView *summaryview,
193 static void summary_display_msg (SummaryView *summaryview,
195 gboolean new_window);
196 static void summary_toggle_view (SummaryView *summaryview);
197 static void summary_set_row_marks (SummaryView *summaryview,
200 /* message handling */
201 static void summary_mark_row (SummaryView *summaryview,
203 static void summary_mark_row_as_read (SummaryView *summaryview,
205 static void summary_mark_row_as_unread (SummaryView *summaryview,
207 static void summary_delete_row (SummaryView *summaryview,
209 static void summary_unmark_row (SummaryView *summaryview,
211 static void summary_move_row_to (SummaryView *summaryview,
213 FolderItem *to_folder);
214 static void summary_copy_row_to (SummaryView *summaryview,
216 FolderItem *to_folder);
218 static void summary_delete_duplicated_func
221 SummaryView *summaryview);
223 static void summary_execute_move (SummaryView *summaryview);
224 static void summary_execute_move_func (GtkCTree *ctree,
227 static void summary_execute_copy (SummaryView *summaryview);
228 static void summary_execute_copy_func (GtkCTree *ctree,
231 static void summary_execute_delete (SummaryView *summaryview);
232 static void summary_execute_delete_func (GtkCTree *ctree,
235 static void summary_ignore_thread(SummaryView *summaryview);
236 static void summary_unignore_thread(SummaryView *summaryview);
238 static void summary_unthread_for_exec (SummaryView *summaryview);
239 static void summary_unthread_for_exec_func (GtkCTree *ctree,
243 static void summary_filter_func (GtkCTree *ctree,
247 static void summary_colorlabel_menu_item_activate_cb
250 static void summary_colorlabel_menu_item_activate_item_cb
251 (GtkMenuItem *label_menu_item,
253 static void summary_colorlabel_menu_create(SummaryView *summaryview);
255 /* callback functions */
256 static void summary_toggle_pressed (GtkWidget *eventbox,
257 GdkEventButton *event,
258 SummaryView *summaryview);
259 static void summary_button_pressed (GtkWidget *ctree,
260 GdkEventButton *event,
261 SummaryView *summaryview);
262 static void summary_button_released (GtkWidget *ctree,
263 GdkEventButton *event,
264 SummaryView *summaryview);
265 static void summary_key_pressed (GtkWidget *ctree,
267 SummaryView *summaryview);
268 static void summary_open_row (GtkSCTree *sctree,
269 SummaryView *summaryview);
270 static void summary_tree_expanded (GtkCTree *ctree,
272 SummaryView *summaryview);
273 static void summary_tree_collapsed (GtkCTree *ctree,
275 SummaryView *summaryview);
276 static void summary_selected (GtkCTree *ctree,
279 SummaryView *summaryview);
280 static void summary_col_resized (GtkCList *clist,
283 SummaryView *summaryview);
284 static void summary_reply_cb (SummaryView *summaryview,
287 static void summary_execute_cb (SummaryView *summaryview,
290 static void summary_show_all_header_cb (SummaryView *summaryview,
294 static void summary_add_address_cb (SummaryView *summaryview,
298 static void summary_mark_clicked (GtkWidget *button,
299 SummaryView *summaryview);
300 static void summary_unread_clicked (GtkWidget *button,
301 SummaryView *summaryview);
302 static void summary_mime_clicked (GtkWidget *button,
303 SummaryView *summaryview);
304 static void summary_num_clicked (GtkWidget *button,
305 SummaryView *summaryview);
306 static void summary_score_clicked (GtkWidget *button,
307 SummaryView *summaryview);
308 static void summary_size_clicked (GtkWidget *button,
309 SummaryView *summaryview);
310 static void summary_date_clicked (GtkWidget *button,
311 SummaryView *summaryview);
312 static void summary_from_clicked (GtkWidget *button,
313 SummaryView *summaryview);
314 static void summary_subject_clicked (GtkWidget *button,
315 SummaryView *summaryview);
317 static void summary_start_drag (GtkWidget *widget,
320 SummaryView *summaryview);
321 static void summary_drag_data_get (GtkWidget *widget,
322 GdkDragContext *drag_context,
323 GtkSelectionData *selection_data,
326 SummaryView *summaryview);
328 /* custom compare functions for sorting */
330 static gint summary_cmp_by_mark (GtkCList *clist,
333 static gint summary_cmp_by_unread (GtkCList *clist,
336 static gint summary_cmp_by_mime (GtkCList *clist,
339 static gint summary_cmp_by_num (GtkCList *clist,
342 static gint summary_cmp_by_size (GtkCList *clist,
345 static gint summary_cmp_by_date (GtkCList *clist,
348 static gint summary_cmp_by_from (GtkCList *clist,
351 static gint summary_cmp_by_subject (GtkCList *clist,
354 static gint summary_cmp_by_score (GtkCList *clist,
357 static gint summary_cmp_by_label (GtkCList *clist,
362 static void summary_mark_all_read (SummaryView *summaryview);
365 GtkTargetEntry summary_drag_types[1] =
367 {"text/plain", GTK_TARGET_SAME_APP, TARGET_DUMMY}
370 static GtkItemFactoryEntry summary_popup_entries[] =
372 {N_("/_Reply"), NULL, summary_reply_cb, COMPOSE_REPLY, NULL},
373 {N_("/Repl_y to sender"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_SENDER, NULL},
374 {N_("/Follow-up and reply to"), NULL, summary_reply_cb, COMPOSE_FOLLOWUP_AND_REPLY_TO, NULL},
375 {N_("/Reply to a_ll"), NULL, summary_reply_cb, COMPOSE_REPLY_TO_ALL, NULL},
376 {N_("/_Forward"), NULL, summary_reply_cb, COMPOSE_FORWARD, NULL},
377 {N_("/Forward as a_ttachment"),
378 NULL, summary_reply_cb, COMPOSE_FORWARD_AS_ATTACH, NULL},
379 {N_("/---"), NULL, NULL, 0, "<Separator>"},
380 {N_("/Re-_edit"), NULL, summary_reedit, 0, NULL},
381 {N_("/---"), NULL, NULL, 0, "<Separator>"},
382 {N_("/M_ove..."), NULL, summary_move_to, 0, NULL},
383 {N_("/_Copy..."), NULL, summary_copy_to, 0, NULL},
384 {N_("/_Delete"), NULL, summary_delete, 0, NULL},
385 {N_("/E_xecute"), NULL, summary_execute_cb, 0, NULL},
386 {N_("/---"), NULL, NULL, 0, "<Separator>"},
387 {N_("/_Mark"), NULL, NULL, 0, "<Branch>"},
388 {N_("/_Mark/_Mark"), NULL, summary_mark, 0, NULL},
389 {N_("/_Mark/_Unmark"), NULL, summary_unmark, 0, NULL},
390 {N_("/_Mark/---"), NULL, NULL, 0, "<Separator>"},
391 {N_("/_Mark/Mark as unr_ead"), NULL, summary_mark_as_unread, 0, NULL},
392 {N_("/_Mark/Mark as rea_d"), NULL, summary_mark_as_read, 0, NULL},
393 {N_("/_Mark/Mark all read"), NULL, summary_mark_all_read, 0, NULL},
394 {N_("/_Mark/Ignore thread"), NULL, summary_ignore_thread, 0, NULL},
395 {N_("/_Mark/Unignore thread"), NULL, summary_unignore_thread, 0, NULL},
396 {N_("/Color la_bel"), NULL, NULL, 0, NULL},
398 {N_("/---"), NULL, NULL, 0, "<Separator>"},
399 {N_("/Add sender to address boo_k"),
400 NULL, summary_add_address_cb, 0, NULL},
401 {N_("/---"), NULL, NULL, 0, "<Separator>"},
402 {N_("/_View"), NULL, NULL, 0, "<Branch>"},
403 {N_("/_View/Open in new _window"),
404 NULL, summary_open_msg, 0, NULL},
405 {N_("/_View/_Source"), NULL, summary_view_source, 0, NULL},
406 {N_("/_View/All _header"), NULL, summary_show_all_header_cb, 0, NULL},
407 {N_("/---"), NULL, NULL, 0, "<Separator>"},
408 {N_("/_Save as..."), NULL, summary_save_as, 0, NULL},
409 {N_("/_Print..."), NULL, summary_print, 0, NULL},
410 {N_("/---"), NULL, NULL, 0, "<Separator>"},
411 {N_("/Select _all"), NULL, summary_select_all, 0, NULL}
414 static const gchar *const col_label[N_SUMMARY_COLS] = {
415 N_("M"), /* S_COL_MARK */
416 N_("U"), /* S_COL_UNREAD */
418 N_("No."), /* S_COL_NUMBER */
419 N_("Score"), /* S_COL_SCORE */
420 N_("Size"), /* S_COL_SIZE */
421 N_("Date"), /* S_COL_DATE */
422 N_("From"), /* S_COL_FROM */
423 N_("Subject") /* S_COL_SUBJECT */
426 SummaryView *summary_create(void)
428 SummaryView *summaryview;
429 SummaryColumnState *col_state;
431 SummaryColumnType col_default[N_SUMMARY_COLS] = {
442 const gchar *titles[N_SUMMARY_COLS];
444 GtkWidget *scrolledwin;
447 GtkWidget *statlabel_folder;
448 GtkWidget *statlabel_select;
449 GtkWidget *statlabel_msgs;
450 GtkWidget *toggle_eventbox;
451 GtkWidget *toggle_arrow;
452 GtkWidget *popupmenu;
453 GtkItemFactory *popupfactory;
457 debug_print(_("Creating summary view...\n"));
458 summaryview = g_new0(SummaryView, 1);
460 vbox = gtk_vbox_new(FALSE, 2);
462 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
463 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
464 GTK_POLICY_AUTOMATIC,
466 gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
467 gtk_widget_set_usize(vbox,
468 prefs_common.summaryview_width,
469 prefs_common.summaryview_height);
471 col_state = summaryview->col_state;
472 col_pos = summaryview->col_pos;
474 for (i = 0; i < N_SUMMARY_COLS; i++) {
475 SummaryColumnType type;
477 type = col_state[i].type = col_default[i];
478 col_state[i].visible = TRUE;
487 if (prefs_common.trans_hdr)
488 titles[i] = gettext(col_label[type]);
490 titles[i] = col_label[type];
493 titles[i] = gettext(col_label[type]);
497 ctree = gtk_sctree_new_with_titles
498 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], (gchar **)titles);
499 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
500 GTK_CLIST(ctree)->hadjustment);
501 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
502 GTK_CLIST(ctree)->vadjustment);
503 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
504 gtk_clist_set_selection_mode(GTK_CLIST(ctree), GTK_SELECTION_EXTENDED);
505 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MARK],
507 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
509 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_MIME],
511 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
513 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
515 gtk_clist_set_column_justification(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
517 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MARK],
518 SUMMARY_COL_MARK_WIDTH);
519 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_UNREAD],
520 SUMMARY_COL_UNREAD_WIDTH);
521 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_MIME],
522 SUMMARY_COL_MIME_WIDTH);
523 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_NUMBER],
524 prefs_common.summary_col_number);
525 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SCORE],
526 prefs_common.summary_col_score);
527 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SIZE],
528 prefs_common.summary_col_size);
529 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_DATE],
530 prefs_common.summary_col_date);
531 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_FROM],
532 prefs_common.summary_col_from);
533 gtk_clist_set_column_width(GTK_CLIST(ctree), col_pos[S_COL_SUBJECT],
534 prefs_common.summary_col_subject);
535 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_DOTTED);
536 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
537 GTK_CTREE_EXPANDER_SQUARE);
539 gtk_ctree_set_line_style(GTK_CTREE(ctree), GTK_CTREE_LINES_NONE);
540 gtk_ctree_set_expander_style(GTK_CTREE(ctree),
541 GTK_CTREE_EXPANDER_TRIANGLE);
543 gtk_ctree_set_indent(GTK_CTREE(ctree), 18);
544 gtk_object_set_user_data(GTK_OBJECT(ctree), summaryview);
546 /* don't let title buttons take key focus */
547 for (i = 0; i < N_SUMMARY_COLS; i++)
548 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(ctree)->column[i].button,
551 /* connect signal to the buttons for sorting */
552 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
554 (GTK_OBJECT(GTK_CLIST(ctree)->column[col_pos[col]].button), \
556 GTK_SIGNAL_FUNC(func), \
559 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK , summary_mark_clicked);
560 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_UNREAD , summary_unread_clicked);
561 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME , summary_mime_clicked);
562 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked);
563 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE , summary_score_clicked);
564 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE , summary_size_clicked);
565 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE , summary_date_clicked);
566 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM , summary_from_clicked);
567 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked);
569 #undef CLIST_BUTTON_SIGNAL_CONNECT
571 /* create status label */
572 hbox = gtk_hbox_new(FALSE, 0);
573 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
575 statlabel_folder = gtk_label_new("");
576 gtk_box_pack_start(GTK_BOX(hbox), statlabel_folder, FALSE, FALSE, 2);
577 statlabel_select = gtk_label_new("");
578 gtk_box_pack_start(GTK_BOX(hbox), statlabel_select, FALSE, FALSE, 16);
580 /* toggle view button */
581 toggle_eventbox = gtk_event_box_new();
582 gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
583 toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
584 gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
586 statlabel_msgs = gtk_label_new("");
587 gtk_box_pack_end(GTK_BOX(hbox), statlabel_msgs, FALSE, FALSE, 4);
589 /* create popup menu */
590 n_entries = sizeof(summary_popup_entries) /
591 sizeof(summary_popup_entries[0]);
592 popupmenu = menu_create_items(summary_popup_entries, n_entries,
593 "<SummaryView>", &popupfactory,
596 /* connect signals */
597 gtk_signal_connect(GTK_OBJECT(ctree), "tree_select_row",
598 GTK_SIGNAL_FUNC(summary_selected), summaryview);
599 gtk_signal_connect(GTK_OBJECT(ctree), "button_press_event",
600 GTK_SIGNAL_FUNC(summary_button_pressed),
602 gtk_signal_connect(GTK_OBJECT(ctree), "button_release_event",
603 GTK_SIGNAL_FUNC(summary_button_released),
605 gtk_signal_connect(GTK_OBJECT(ctree), "key_press_event",
606 GTK_SIGNAL_FUNC(summary_key_pressed), summaryview);
607 gtk_signal_connect(GTK_OBJECT(ctree), "resize_column",
608 GTK_SIGNAL_FUNC(summary_col_resized), summaryview);
609 gtk_signal_connect(GTK_OBJECT(ctree), "open_row",
610 GTK_SIGNAL_FUNC(summary_open_row), summaryview);
612 gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_expand",
613 GTK_SIGNAL_FUNC(summary_tree_expanded),
615 gtk_signal_connect_after(GTK_OBJECT(ctree), "tree_collapse",
616 GTK_SIGNAL_FUNC(summary_tree_collapsed),
619 gtk_signal_connect(GTK_OBJECT(ctree), "start_drag",
620 GTK_SIGNAL_FUNC(summary_start_drag),
622 gtk_signal_connect(GTK_OBJECT(ctree), "drag_data_get",
623 GTK_SIGNAL_FUNC(summary_drag_data_get),
626 gtk_signal_connect(GTK_OBJECT(toggle_eventbox), "button_press_event",
627 GTK_SIGNAL_FUNC(summary_toggle_pressed),
630 summaryview->vbox = vbox;
631 summaryview->scrolledwin = scrolledwin;
632 summaryview->ctree = ctree;
633 summaryview->hbox = hbox;
634 summaryview->statlabel_folder = statlabel_folder;
635 summaryview->statlabel_select = statlabel_select;
636 summaryview->statlabel_msgs = statlabel_msgs;
637 summaryview->toggle_eventbox = toggle_eventbox;
638 summaryview->toggle_arrow = toggle_arrow;
639 summaryview->popupmenu = popupmenu;
640 summaryview->popupfactory = popupfactory;
641 summaryview->msg_is_toggled_on = TRUE;
642 summaryview->lock_count = 0;
643 summaryview->sort_mode = SORT_BY_NONE;
644 summaryview->sort_type = GTK_SORT_ASCENDING;
646 summary_change_display_item(summaryview);
648 gtk_widget_show_all(vbox);
653 void summary_init(SummaryView *summaryview)
658 PIXMAP_CREATE(summaryview->ctree, markxpm, markxpmmask, mark_xpm);
659 PIXMAP_CREATE(summaryview->ctree, deletedxpm, deletedxpmmask,
661 PIXMAP_CREATE(summaryview->ctree, newxpm, newxpmmask, new_xpm);
662 PIXMAP_CREATE(summaryview->ctree, unreadxpm, unreadxpmmask, unread_xpm);
663 PIXMAP_CREATE(summaryview->ctree, repliedxpm, repliedxpmmask,
665 PIXMAP_CREATE(summaryview->ctree, forwardedxpm, forwardedxpmmask,
667 PIXMAP_CREATE(summaryview->ctree, ignorethreadxpm, ignorethreadxpmmask,
669 PIXMAP_CREATE(summaryview->ctree, clipxpm, clipxpmmask, clip_xpm);
670 PIXMAP_CREATE(summaryview->hbox, folderxpm, folderxpmmask,
673 pixmap = gtk_pixmap_new(clipxpm, clipxpmmask);
674 gtk_clist_set_column_widget(GTK_CLIST(summaryview->ctree),
675 summaryview->col_pos[S_COL_MIME], pixmap);
676 gtk_widget_show(pixmap);
679 small_style = gtk_style_copy
680 (gtk_widget_get_style(summaryview->ctree));
682 smallfont = gdk_fontset_load(SMALL_FONT);
683 small_style->font = smallfont;
684 small_marked_style = gtk_style_copy(small_style);
685 small_marked_style->fg[GTK_STATE_NORMAL] =
686 summaryview->color_marked;
687 small_deleted_style = gtk_style_copy(small_style);
688 small_deleted_style->fg[GTK_STATE_NORMAL] =
689 summaryview->color_dim;
692 bold_style = gtk_style_copy
693 (gtk_widget_get_style(summaryview->ctree));
695 boldfont = gdk_fontset_load(BOLD_FONT);
696 bold_style->font = boldfont;
697 bold_marked_style = gtk_style_copy(bold_style);
698 bold_marked_style->fg[GTK_STATE_NORMAL] =
699 summaryview->color_marked;
700 bold_deleted_style = gtk_style_copy(bold_style);
701 bold_deleted_style->fg[GTK_STATE_NORMAL] =
702 summaryview->color_dim;
705 style = gtk_style_copy(gtk_widget_get_style
706 (summaryview->statlabel_folder));
708 gtk_widget_set_style(summaryview->statlabel_folder, style);
709 gtk_widget_set_style(summaryview->statlabel_select, style);
710 gtk_widget_set_style(summaryview->statlabel_msgs, style);
712 pixmap = gtk_pixmap_new(folderxpm, folderxpmmask);
713 gtk_box_pack_start(GTK_BOX(summaryview->hbox), pixmap, FALSE, FALSE, 4);
714 gtk_box_reorder_child(GTK_BOX(summaryview->hbox), pixmap, 0);
715 gtk_widget_show(pixmap);
717 summary_clear_list(summaryview);
718 summary_colorlabel_menu_create(summaryview);
719 summary_set_menu_sensitive(summaryview);
723 GtkCTreeNode * summary_find_next_important_score(SummaryView *summaryview,
724 GtkCTreeNode *current_node)
726 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
729 gint best_score = MIN_SCORE;
730 GtkCTreeNode *best_node = NULL;
733 /*node = current_node;*/
734 node = GTK_CTREE_NODE_NEXT(current_node);
736 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
738 for (; node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
739 msginfo = gtk_ctree_node_get_row_data(ctree, node);
740 if (msginfo->score >= summaryview->important_score)
742 if (msginfo->score > best_score) {
743 best_score = msginfo->score;
754 GtkCTreeNode * summary_find_prev_important_score(SummaryView *summaryview,
755 GtkCTreeNode *current_node)
757 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
760 gint best_score = MIN_SCORE;
761 GtkCTreeNode *best_node = NULL;
764 /*node = current_node;*/
765 node = GTK_CTREE_NODE_PREV(current_node);
767 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
769 for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
770 msginfo = gtk_ctree_node_get_row_data(ctree, node);
771 if (msginfo->score >= summaryview->important_score)
773 if (msginfo->score > best_score) {
774 best_score = msginfo->score;
785 gboolean summary_show(SummaryView *summaryview, FolderItem *item,
786 gboolean update_cache)
788 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
790 GSList *mlist = NULL;
793 guint selected_msgnum = 0;
794 guint displayed_msgnum = 0;
795 GtkCTreeNode *selected_node = summaryview->folderview->selected;
799 static gboolean locked = FALSE;
801 if (summary_is_locked(summaryview)) return FALSE;
804 summary_lock(summaryview);
806 STATUSBAR_POP(summaryview->mainwin);
808 is_refresh = (!prefs_common.open_inbox_on_inc &&
809 item == summaryview->folder_item &&
810 update_cache == FALSE) ? TRUE : FALSE;
812 selected_msgnum = summary_get_msgnum(summaryview,
813 summaryview->selected);
814 displayed_msgnum = summary_get_msgnum(summaryview,
815 summaryview->displayed);
818 /* process the marks if any */
819 if (summaryview->mainwin->lock_count == 0 &&
820 (summaryview->moved > 0 || summaryview->copied > 0)) {
823 val = alertpanel(_("Process mark"),
824 _("Some marks are left. Process it?"),
825 _("Yes"), _("No"), _("Cancel"));
826 if (G_ALERTDEFAULT == val)
827 summary_execute(summaryview);
828 else if (G_ALERTALTERNATE == val)
829 summary_write_cache(summaryview);
831 summary_unlock(summaryview);
835 folder_update_op_count();
837 else if (!summaryview->filtering_happened) {
838 summary_write_cache(summaryview);
841 summaryview->filtering_happened = FALSE;
843 summaryview->folderview->opened = selected_node;
845 gtk_clist_freeze(GTK_CLIST(ctree));
847 summary_clear_list(summaryview);
848 summary_set_menu_sensitive(summaryview);
850 messageview_clear(summaryview->messageview);
853 if (!item || !item->path || !item->parent || item->no_select ||
854 (item->folder->type == F_MH &&
855 ((buf = folder_item_get_path(item)) == NULL ||
856 change_dir(buf) < 0))) {
858 debug_print(_("empty folder\n\n"));
860 messageview_clear(summaryview->messageview);
861 summary_clear_all(summaryview);
862 summaryview->folder_item = item;
863 gtk_clist_thaw(GTK_CLIST(ctree));
864 summary_unlock(summaryview);
870 summaryview->folder_item = item;
872 gtk_signal_handler_block_by_data(GTK_OBJECT(ctree), summaryview);
874 buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
875 debug_print("%s\n", buf);
876 STATUSBAR_PUSH(summaryview->mainwin, buf);
879 main_window_cursor_wait(summaryview->mainwin);
881 mlist = item->folder->get_msg_list(item->folder, item, !update_cache);
883 for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
884 MsgInfo * msginfo = (MsgInfo *) cur->data;
886 msginfo->score = score_message(global_scoring, msginfo);
887 if (msginfo->score != MAX_SCORE &&
888 msginfo->score != MIN_SCORE) {
889 msginfo->score += score_message(item->prefs->scoring,
894 summaryview->killed_messages = NULL;
895 if ((global_scoring || item->prefs->scoring) &&
896 (item->folder->type == F_NEWS)) {
901 kill_score = prefs_common.kill_score;
902 if (item->prefs->kill_score > kill_score)
903 kill_score = item->prefs->kill_score;
904 for(cur = mlist ; cur != NULL ; cur = g_slist_next(cur)) {
905 MsgInfo * msginfo = (MsgInfo *) cur->data;
907 if (MSG_IS_NEWS(msginfo->flags) &&
908 (msginfo->score <= kill_score))
909 summaryview->killed_messages = g_slist_append(summaryview->killed_messages, msginfo);
911 not_killed = g_slist_append(not_killed,
918 STATUSBAR_POP(summaryview->mainwin);
920 /* set ctree and hash table from the msginfo list
921 creating thread, and count the number of messages */
922 summary_set_ctree_from_list(summaryview, mlist);
926 summary_write_cache(summaryview);
928 gtk_signal_handler_unblock_by_data(GTK_OBJECT(ctree), summaryview);
930 gtk_clist_thaw(GTK_CLIST(ctree));
933 sort_mode = prefs_folder_item_get_sort_mode(item);
934 sort_type = prefs_folder_item_get_sort_type(item);
936 if (sort_mode != SORT_BY_NONE) {
937 summaryview->sort_mode = sort_mode;
938 if (sort_type == GTK_SORT_DESCENDING)
939 summaryview->sort_type = GTK_SORT_ASCENDING;
941 summaryview->sort_type = GTK_SORT_DESCENDING;
943 summary_sort(summaryview, sort_mode);
947 summaryview->displayed =
948 summary_find_msg_by_msgnum(summaryview,
950 if (!summaryview->displayed)
951 messageview_clear(summaryview->messageview);
952 summary_select_by_msgnum(summaryview, selected_msgnum);
953 if (!summaryview->selected) {
954 /* no selected message - select first unread
955 message, but do not display it */
956 node = summary_find_next_unread_msg(summaryview, NULL);
957 if (node == NULL && GTK_CLIST(ctree)->row_list != NULL)
958 node = gtk_ctree_node_nth
959 (ctree, GTK_CLIST(ctree)->rows - 1);
960 summary_select_node(summaryview, node, FALSE);
963 /* select first unread message */
964 if (sort_mode == SORT_BY_SCORE)
965 node = summary_find_next_important_score(summaryview,
968 node = summary_find_next_unread_msg(summaryview, NULL);
970 if (node == NULL && GTK_CLIST(ctree)->row_list != NULL) {
971 /* Get the last visible node on screen */
972 /* FIXME: huh, what happens if node is null? that allowed?? */
973 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
975 if (prefs_common.open_unread_on_enter) {
976 summary_unlock(summaryview);
977 summary_select_node(summaryview, node, TRUE);
978 summary_lock(summaryview);
980 summary_select_node(summaryview, node, FALSE);
983 summary_status_show(summaryview);
985 summary_set_menu_sensitive(summaryview);
987 main_window_set_toolbar_sensitive(summaryview->mainwin);
990 STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
992 main_window_cursor_normal(summaryview->mainwin);
993 summary_unlock(summaryview);
999 void summary_clear_list(SummaryView *summaryview)
1001 GtkCList *clist = GTK_CLIST(summaryview->ctree);
1005 gtk_clist_freeze(clist);
1007 for(cur = summaryview->killed_messages ; cur != NULL ;
1008 cur = g_slist_next(cur)) {
1009 MsgInfo * msginfo = (MsgInfo *) cur->data;
1011 procmsg_msginfo_free(msginfo);
1013 g_slist_free(summaryview->killed_messages);
1014 summaryview->killed_messages = NULL;
1016 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree),
1017 NULL, summary_free_msginfo_func, NULL);
1019 summaryview->folder_item = NULL;
1021 summaryview->display_msg = FALSE;
1023 summaryview->selected = NULL;
1024 summaryview->displayed = NULL;
1025 summaryview->newmsgs = summaryview->unread = 0;
1026 summaryview->messages = summaryview->total_size = 0;
1027 summaryview->deleted = summaryview->moved = 0;
1028 summaryview->copied = 0;
1029 if (summaryview->msgid_table) {
1030 g_hash_table_destroy(summaryview->msgid_table);
1031 summaryview->msgid_table = NULL;
1033 if (summaryview->subject_table) {
1034 g_hash_table_destroy(summaryview->subject_table);
1035 summaryview->subject_table = NULL;
1037 summaryview->mlist = NULL;
1038 if (summaryview->folder_table) {
1039 g_hash_table_destroy(summaryview->folder_table);
1040 summaryview->folder_table = NULL;
1042 summaryview->sort_mode = SORT_BY_NONE;
1043 summaryview->sort_type = GTK_SORT_ASCENDING;
1045 gtk_clist_clear(clist);
1046 if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1047 optimal_width = gtk_clist_optimal_column_width
1048 (clist, summaryview->col_pos[S_COL_SUBJECT]);
1049 gtk_clist_set_column_width
1050 (clist, summaryview->col_pos[S_COL_SUBJECT],
1054 gtk_clist_thaw(clist);
1057 void summary_clear_all(SummaryView *summaryview)
1059 summary_clear_list(summaryview);
1060 summary_set_menu_sensitive(summaryview);
1061 main_window_set_toolbar_sensitive(summaryview->mainwin);
1062 summary_status_show(summaryview);
1065 void summary_lock(SummaryView *summaryview)
1067 summaryview->lock_count++;
1070 void summary_unlock(SummaryView *summaryview)
1072 if (summaryview->lock_count)
1073 summaryview->lock_count--;
1076 gboolean summary_is_locked(SummaryView *summaryview)
1078 return summaryview->lock_count > 0;
1081 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1083 GtkCList *clist = GTK_CLIST(summaryview->ctree);
1084 SummarySelection selection;
1086 if (!clist->row_list)
1087 selection = SUMMARY_NONE;
1088 else if (!clist->selection)
1089 selection = SUMMARY_SELECTED_NONE;
1090 else if (!clist->selection->next)
1091 selection = SUMMARY_SELECTED_SINGLE;
1093 selection = SUMMARY_SELECTED_MULTIPLE;
1098 static void summary_set_menu_sensitive(SummaryView *summaryview)
1100 GtkItemFactory *ifactory = summaryview->popupfactory;
1101 SummarySelection selection;
1104 selection = summary_get_selection_type(summaryview);
1105 main_window_set_menu_sensitive(summaryview->mainwin);
1107 if (selection == SUMMARY_NONE) {
1110 submenu = gtk_item_factory_get_widget
1111 (summaryview->popupfactory, "/Mark");
1112 menu_set_insensitive_all(GTK_MENU_SHELL(submenu));
1113 menu_set_insensitive_all
1114 (GTK_MENU_SHELL(summaryview->popupmenu));
1118 if (summaryview->folder_item->folder->type != F_NEWS) {
1119 menu_set_sensitive(ifactory, "/Move...", TRUE);
1120 menu_set_sensitive(ifactory, "/Copy...", TRUE);
1121 menu_set_sensitive(ifactory, "/Delete", TRUE);
1124 menu_set_sensitive(ifactory, "/Execute", TRUE);
1126 menu_set_sensitive(ifactory, "/Mark", TRUE);
1127 menu_set_sensitive(ifactory, "/Mark/Mark", TRUE);
1128 menu_set_sensitive(ifactory, "/Mark/Unmark", TRUE);
1130 menu_set_sensitive(ifactory, "/Mark/Mark as unread", TRUE);
1131 menu_set_sensitive(ifactory, "/Mark/Mark as read", TRUE);
1132 menu_set_sensitive(ifactory, "/Mark/Mark all read", TRUE);
1133 menu_set_sensitive(ifactory, "/Mark/Ignore thread", TRUE);
1134 menu_set_sensitive(ifactory, "/Mark/Unignore thread", TRUE);
1136 menu_set_sensitive(ifactory, "/Color label", TRUE);
1138 sens = (selection == SUMMARY_SELECTED_MULTIPLE) ? FALSE : TRUE;
1139 menu_set_sensitive(ifactory, "/Reply", sens);
1140 menu_set_sensitive(ifactory, "/Reply to sender", sens);
1141 menu_set_sensitive(ifactory, "/Reply to all", sens);
1142 menu_set_sensitive(ifactory, "/Forward", TRUE);
1143 menu_set_sensitive(ifactory, "/Forward as attachment", TRUE);
1145 menu_set_sensitive(ifactory, "/Add sender to address book", sens);
1147 menu_set_sensitive(ifactory, "/View", sens);
1148 menu_set_sensitive(ifactory, "/View/Open in new window", sens);
1149 menu_set_sensitive(ifactory, "/View/Source", sens);
1150 menu_set_sensitive(ifactory, "/View/All header", sens);
1151 if ((summaryview->folder_item->stype == F_DRAFT) ||
1152 (summaryview->folder_item->stype == F_OUTBOX) ||
1153 (summaryview->folder_item->stype == F_QUEUE))
1154 menu_set_sensitive(ifactory, "/Re-edit", sens);
1156 menu_set_sensitive(ifactory, "/Save as...", sens);
1157 menu_set_sensitive(ifactory, "/Print...", TRUE);
1159 menu_set_sensitive(ifactory, "/Select all", TRUE);
1161 if (summaryview->folder_item->folder->account)
1162 sens = summaryview->folder_item->folder->account->protocol
1166 menu_set_sensitive(ifactory, "/Follow-up and reply to", sens);
1169 void summary_select_prev_unread(SummaryView *summaryview)
1172 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1174 node = summary_find_prev_unread_msg(summaryview, summaryview->selected);
1179 switch (prefs_common.next_unread_msg_dialog) {
1180 case NEXTUNREADMSGDIALOG_ALWAYS:
1181 val = alertpanel(_("No more unread messages"),
1182 _("No unread message found. "
1183 "Search from the end?"),
1184 _("Yes"), _("No"), NULL);
1186 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1187 val = G_ALERTDEFAULT;
1189 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1190 val = !G_ALERTDEFAULT;
1194 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1196 if (val != G_ALERTDEFAULT) return;
1197 node = summary_find_prev_unread_msg(summaryview, NULL);
1201 alertpanel_notice(_("No unread messages."));
1203 gtkut_ctree_expand_parent_all(ctree, node);
1204 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1205 gtk_sctree_select(GTK_SCTREE(ctree), node);
1206 if (summaryview->displayed == node)
1207 summaryview->displayed = NULL;
1208 summary_display_msg(summaryview, node, FALSE);
1212 void summary_select_next_unread(SummaryView *summaryview)
1215 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1217 node = summary_find_next_unread_msg(summaryview, summaryview->selected);
1220 gtkut_ctree_expand_parent_all(ctree, node);
1221 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1222 gtk_sctree_select(GTK_SCTREE(ctree), node);
1223 if (summaryview->displayed == node)
1224 summaryview->displayed = NULL;
1225 summary_display_msg(summaryview, node, FALSE);
1229 switch (prefs_common.next_unread_msg_dialog) {
1230 case NEXTUNREADMSGDIALOG_ALWAYS:
1231 val = alertpanel(_("No more unread messages"),
1232 _("No unread message found. "
1233 "Go to next folder?"),
1234 _("Yes"), _("No"), NULL);
1236 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1237 val = G_ALERTDEFAULT;
1239 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1240 val = !G_ALERTDEFAULT;
1244 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1247 if (val == G_ALERTDEFAULT) {
1248 if (gtk_signal_n_emissions_by_name
1249 (GTK_OBJECT(ctree), "key_press_event") > 0)
1250 gtk_signal_emit_stop_by_name
1253 folderview_select_next_unread(summaryview->folderview);
1258 void summary_select_prev_marked(SummaryView *summaryview)
1261 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1263 node = summary_find_prev_marked_msg(summaryview, summaryview->selected);
1268 val = alertpanel(_("No more marked messages"),
1269 _("No marked message found. "
1270 "Search from the end?"),
1271 _("Yes"), _("No"), NULL);
1272 if (val != G_ALERTDEFAULT) return;
1273 node = summary_find_prev_marked_msg(summaryview, NULL);
1277 alertpanel_notice(_("No marked messages."));
1279 gtkut_ctree_expand_parent_all(ctree, node);
1280 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1281 gtk_sctree_select(GTK_SCTREE(ctree), node);
1282 if (summaryview->displayed == node)
1283 summaryview->displayed = NULL;
1284 summary_display_msg(summaryview, node, FALSE);
1288 void summary_select_next_marked(SummaryView *summaryview)
1291 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1293 node = summary_find_next_marked_msg(summaryview, summaryview->selected);
1298 val = alertpanel(_("No more marked messages"),
1299 _("No marked message found. "
1300 "Search from the beginning?"),
1301 _("Yes"), _("No"), NULL);
1302 if (val != G_ALERTDEFAULT) return;
1303 node = summary_find_next_marked_msg(summaryview, NULL);
1307 alertpanel_notice(_("No marked messages."));
1309 gtkut_ctree_expand_parent_all(ctree, node);
1310 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1311 gtk_sctree_select(GTK_SCTREE(ctree), node);
1312 if (summaryview->displayed == node)
1313 summaryview->displayed = NULL;
1314 summary_display_msg(summaryview, node, FALSE);
1318 void summary_select_prev_labeled(SummaryView *summaryview)
1321 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1323 node = summary_find_prev_labeled_msg(summaryview, summaryview->selected);
1328 val = alertpanel(_("No more labeled messages"),
1329 _("No labeled message found. "
1330 "Search from the end?"),
1331 _("Yes"), _("No"), NULL);
1332 if (val != G_ALERTDEFAULT) return;
1333 node = summary_find_prev_labeled_msg(summaryview, NULL);
1337 alertpanel_notice(_("No labeled messages."));
1339 gtkut_ctree_expand_parent_all(ctree, node);
1340 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1341 gtk_sctree_select(GTK_SCTREE(ctree), node);
1342 if (summaryview->displayed == node)
1343 summaryview->displayed = NULL;
1344 summary_display_msg(summaryview, node, FALSE);
1348 void summary_select_next_labeled(SummaryView *summaryview)
1351 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1353 node = summary_find_next_labeled_msg(summaryview, summaryview->selected);
1358 val = alertpanel(_("No more labeled messages"),
1359 _("No labeled message found. "
1360 "Search from the beginning?"),
1361 _("Yes"), _("No"), NULL);
1362 if (val != G_ALERTDEFAULT) return;
1363 node = summary_find_next_labeled_msg(summaryview, NULL);
1367 alertpanel_notice(_("No labeled messages."));
1369 gtkut_ctree_expand_parent_all(ctree, node);
1370 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1371 gtk_sctree_select(GTK_SCTREE(ctree), node);
1372 if (summaryview->displayed == node)
1373 summaryview->displayed = NULL;
1374 summary_display_msg(summaryview, node, FALSE);
1378 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum)
1382 node = summary_find_msg_by_msgnum(summaryview, msgnum);
1383 summary_select_node(summaryview, node, FALSE);
1387 * summary_select_node:
1388 * @summaryview: Summary view.
1389 * @node: Summary tree node.
1390 * @display_msg: TRUE to display the selected message.
1392 * Select @node (bringing it into view by scrolling and expanding its
1393 * thread, if necessary) and unselect all others. If @display_msg is
1394 * TRUE, display the corresponding message in the message view.
1396 void summary_select_node(SummaryView *summaryview, GtkCTreeNode *node,
1397 gboolean display_msg)
1399 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1403 gtkut_ctree_expand_parent_all(ctree, node);
1404 gtk_ctree_node_moveto(ctree, node, -1, 0.5, 0);
1405 gtk_widget_grab_focus(GTK_WIDGET(ctree));
1406 gtk_sctree_unselect_all(GTK_SCTREE(ctree));
1407 summaryview->display_msg = display_msg;
1408 gtk_sctree_select(GTK_SCTREE(ctree), node);
1412 static guint summary_get_msgnum(SummaryView *summaryview, GtkCTreeNode *node)
1414 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1419 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1420 return msginfo->msgnum;
1423 static GtkCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
1424 GtkCTreeNode *current_node)
1426 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1431 node = current_node;
1433 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1435 for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1436 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1437 if (!MSG_IS_DELETED(msginfo->flags)) break;
1443 static GtkCTreeNode *summary_find_next_msg(SummaryView *summaryview,
1444 GtkCTreeNode *current_node)
1446 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1451 node = current_node;
1453 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1455 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1456 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1457 if (!MSG_IS_DELETED(msginfo->flags)) break;
1463 static GtkCTreeNode *summary_find_prev_unread_msg(SummaryView *summaryview,
1464 GtkCTreeNode *current_node)
1466 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1471 node = current_node;
1473 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1475 for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1476 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1477 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1483 static GtkCTreeNode *summary_find_next_unread_msg(SummaryView *summaryview,
1484 GtkCTreeNode *current_node)
1486 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1491 node = current_node;
1493 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1495 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1496 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1497 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) break;
1503 static GtkCTreeNode *summary_find_prev_marked_msg(SummaryView *summaryview,
1504 GtkCTreeNode *current_node)
1506 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1511 node = GTK_CTREE_NODE_PREV(current_node);
1513 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1515 for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1516 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1517 if (MSG_IS_MARKED(msginfo->flags)) break;
1523 static GtkCTreeNode *summary_find_next_marked_msg(SummaryView *summaryview,
1524 GtkCTreeNode *current_node)
1526 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1531 node = gtkut_ctree_node_next(ctree, current_node);
1533 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1535 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1536 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1537 if (MSG_IS_MARKED(msginfo->flags)) break;
1543 static GtkCTreeNode *summary_find_prev_labeled_msg(SummaryView *summaryview,
1544 GtkCTreeNode *current_node)
1546 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1551 node = GTK_CTREE_NODE_PREV(current_node);
1553 node = gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->rows - 1);
1555 for (; node != NULL; node = GTK_CTREE_NODE_PREV(node)) {
1556 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1557 if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) > 0) break;
1563 static GtkCTreeNode *summary_find_next_labeled_msg(SummaryView *summaryview,
1564 GtkCTreeNode *current_node)
1566 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1571 node = gtkut_ctree_node_next(ctree, current_node);
1573 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1575 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1576 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1577 if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) > 0) break;
1583 static GtkCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
1586 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1590 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1592 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1593 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1594 if (msginfo->msgnum == msgnum) break;
1600 static guint attract_hash_func(gconstpointer key)
1606 Xstrdup_a(str, (const gchar *)key, return 0);
1613 for (p += 1; *p != '\0'; p++)
1614 h = (h << 5) - h + *p;
1620 static gint attract_compare_func(gconstpointer a, gconstpointer b)
1622 return subject_compare((const gchar *)a, (const gchar *)b) == 0;
1625 void summary_attract_by_subject(SummaryView *summaryview)
1627 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1628 GtkCList *clist = GTK_CLIST(ctree);
1629 GtkCTreeNode *src_node;
1630 GtkCTreeNode *dst_node, *sibling;
1632 MsgInfo *src_msginfo, *dst_msginfo;
1633 GHashTable *subject_table;
1635 debug_print(_("Attracting messages by subject..."));
1636 STATUSBAR_PUSH(summaryview->mainwin,
1637 _("Attracting messages by subject..."));
1639 main_window_cursor_wait(summaryview->mainwin);
1640 gtk_clist_freeze(clist);
1642 subject_table = g_hash_table_new(attract_hash_func,
1643 attract_compare_func);
1645 for (src_node = GTK_CTREE_NODE(clist->row_list);
1648 tmp = GTK_CTREE_ROW(src_node)->sibling;
1649 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
1650 if (!src_msginfo) continue;
1651 if (!src_msginfo->subject) continue;
1653 /* find attracting node */
1654 dst_node = g_hash_table_lookup(subject_table,
1655 src_msginfo->subject);
1658 dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
1660 /* if the time difference is more than 20 days,
1662 if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
1663 > 60 * 60 * 24 * 20)
1666 sibling = GTK_CTREE_ROW(dst_node)->sibling;
1667 if (src_node != sibling)
1668 gtk_ctree_move(ctree, src_node, NULL, sibling);
1671 g_hash_table_insert(subject_table,
1672 src_msginfo->subject, src_node);
1675 g_hash_table_destroy(subject_table);
1677 gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1679 gtk_clist_thaw(clist);
1681 debug_print(_("done.\n"));
1682 STATUSBAR_POP(summaryview->mainwin);
1684 main_window_cursor_normal(summaryview->mainwin);
1687 static void summary_free_msginfo_func(GtkCTree *ctree, GtkCTreeNode *node,
1690 MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
1693 procmsg_msginfo_free(msginfo);
1696 static void summary_set_marks_func(GtkCTree *ctree, GtkCTreeNode *node,
1699 SummaryView *summaryview = data;
1702 msginfo = gtk_ctree_node_get_row_data(ctree, node);
1704 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1705 summaryview->newmsgs++;
1706 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1707 summaryview->unread++;
1708 if (MSG_IS_DELETED(msginfo->flags))
1709 summaryview->deleted++;
1711 summaryview->messages++;
1712 summaryview->total_size += msginfo->size;
1714 summary_set_row_marks(summaryview, node);
1717 static void summary_update_status(SummaryView *summaryview)
1719 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1723 summaryview->newmsgs = summaryview->unread =
1724 summaryview->messages = summaryview->total_size =
1725 summaryview->deleted = summaryview->moved = summaryview->copied = 0;
1727 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
1728 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1729 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
1731 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
1732 summaryview->newmsgs++;
1733 if (MSG_IS_UNREAD(msginfo->flags)&& !MSG_IS_IGNORE_THREAD(msginfo->flags))
1734 summaryview->unread++;
1735 if (MSG_IS_DELETED(msginfo->flags))
1736 summaryview->deleted++;
1737 if (MSG_IS_MOVE(msginfo->flags))
1738 summaryview->moved++;
1739 if (MSG_IS_COPY(msginfo->flags))
1740 summaryview->copied++;
1741 summaryview->messages++;
1742 summaryview->total_size += msginfo->size;
1746 static void summary_status_show(SummaryView *summaryview)
1749 gchar *del, *mv, *cp;
1752 GList *rowlist, *cur;
1753 guint n_selected = 0;
1757 if (!summaryview->folder_item) {
1758 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder), "");
1759 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), "");
1760 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), "");
1764 rowlist = GTK_CLIST(summaryview->ctree)->selection;
1765 for (cur = rowlist; cur != NULL; cur = cur->next) {
1766 msginfo = gtk_ctree_node_get_row_data
1767 (GTK_CTREE(summaryview->ctree),
1768 GTK_CTREE_NODE(cur->data));
1769 sel_size += msginfo->size;
1773 gtk_label_set(GTK_LABEL(summaryview->statlabel_folder),
1774 summaryview->folder_item &&
1775 summaryview->folder_item->folder->type == F_NEWS
1776 ? g_basename(summaryview->folder_item->path)
1777 : summaryview->folder_item->path);
1779 if (summaryview->deleted)
1780 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
1783 if (summaryview->moved)
1784 mv = g_strdup_printf(_("%s%d moved"),
1785 summaryview->deleted ? _(", ") : "",
1786 summaryview->moved);
1789 if (summaryview->copied)
1790 cp = g_strdup_printf(_("%s%d copied"),
1791 summaryview->deleted ||
1792 summaryview->moved ? _(", ") : "",
1793 summaryview->copied);
1797 if (summaryview->deleted || summaryview->moved || summaryview->copied)
1803 sel = g_strdup_printf(" (%s)", to_human_readable(sel_size));
1806 str = g_strconcat(n_selected ? itos(n_selected) : "",
1807 n_selected ? _(" item(s) selected") : "",
1808 sel, spc, del, mv, cp, NULL);
1809 gtk_label_set(GTK_LABEL(summaryview->statlabel_select), str);
1816 if (summaryview->folder_item &&
1817 FOLDER_IS_LOCAL(summaryview->folder_item->folder)) {
1818 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
1819 summaryview->newmsgs,
1820 summaryview->unread,
1821 summaryview->messages,
1822 to_human_readable(summaryview->total_size));
1824 str = g_strdup_printf(_("%d new, %d unread, %d total"),
1825 summaryview->newmsgs,
1826 summaryview->unread,
1827 summaryview->messages);
1829 gtk_label_set(GTK_LABEL(summaryview->statlabel_msgs), str);
1832 folderview_update_msg_num(summaryview->folderview,
1833 summaryview->folderview->opened,
1834 summaryview->newmsgs,
1835 summaryview->unread,
1836 summaryview->messages);
1839 void summary_sort(SummaryView *summaryview, SummarySortType type)
1841 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1842 GtkCList *clist = GTK_CLIST(summaryview->ctree);
1843 GtkCListCompareFunc cmp_func;
1845 if (!summaryview->folder_item)
1850 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mark;
1852 case SORT_BY_UNREAD:
1853 cmp_func = (GtkCListCompareFunc)summary_cmp_by_unread;
1856 cmp_func = (GtkCListCompareFunc)summary_cmp_by_mime;
1858 case SORT_BY_NUMBER:
1859 cmp_func = (GtkCListCompareFunc)summary_cmp_by_num;
1862 cmp_func = (GtkCListCompareFunc)summary_cmp_by_size;
1865 cmp_func = (GtkCListCompareFunc)summary_cmp_by_date;
1868 cmp_func = (GtkCListCompareFunc)summary_cmp_by_from;
1870 case SORT_BY_SUBJECT:
1871 cmp_func = (GtkCListCompareFunc)summary_cmp_by_subject;
1874 cmp_func = (GtkCListCompareFunc)summary_cmp_by_score;
1877 cmp_func = (GtkCListCompareFunc)summary_cmp_by_label;
1883 debug_print(_("Sorting summary..."));
1884 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
1886 main_window_cursor_wait(summaryview->mainwin);
1888 gtk_clist_set_compare_func(clist, cmp_func);
1890 /* toggle sort type if the same column is selected */
1891 if (summaryview->sort_mode == type)
1892 summaryview->sort_type =
1893 summaryview->sort_type == GTK_SORT_ASCENDING
1894 ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
1896 summaryview->sort_type = GTK_SORT_ASCENDING;
1897 gtk_clist_set_sort_type(clist, summaryview->sort_type);
1898 summaryview->sort_mode = type;
1900 gtk_ctree_sort_node(ctree, NULL);
1902 gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
1903 /*gtkut_ctree_set_focus_row(ctree, summaryview->selected);*/
1905 prefs_folder_item_set_config(summaryview->folder_item,
1906 summaryview->sort_type,
1907 summaryview->sort_mode);
1908 prefs_folder_item_save_config(summaryview->folder_item);
1910 debug_print(_("done.\n"));
1911 STATUSBAR_POP(summaryview->mainwin);
1913 main_window_cursor_normal(summaryview->mainwin);
1916 static GtkCTreeNode * subject_table_lookup(GHashTable *subject_table,
1919 if (g_strncasecmp(subject, "Re: ", 4) == 0)
1920 return g_hash_table_lookup(subject_table, subject + 4);
1922 return g_hash_table_lookup(subject_table, subject);
1925 static void subject_table_insert(GHashTable *subject_table, gchar * subject,
1926 GtkCTreeNode * node)
1928 if (g_strncasecmp(subject, "Re: ", 4) == 0)
1929 g_hash_table_insert(subject_table, subject + 4, node);
1931 g_hash_table_insert(subject_table, subject, node);
1934 static void summary_set_ctree_from_list(SummaryView *summaryview,
1937 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
1939 MsgInfo *parentinfo;
1940 MsgInfo *cur_msginfo;
1941 GtkCTreeNode *node, *parent;
1942 gchar *text[N_SUMMARY_COLS];
1943 GHashTable *msgid_table;
1944 GHashTable *subject_table;
1946 GtkCTreeNode *cur_parent;
1950 debug_print(_("\tSetting summary from message data..."));
1951 STATUSBAR_PUSH(summaryview->mainwin,
1952 _("Setting summary from message data..."));
1955 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
1956 summaryview->msgid_table = msgid_table;
1957 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
1958 summaryview->subject_table = subject_table;
1960 if (prefs_common.use_addr_book)
1961 start_address_completion();
1963 for (cur = mlist ; cur != NULL; cur = cur->next) {
1964 msginfo = (MsgInfo *)cur->data;
1965 msginfo->threadscore = msginfo->score;
1968 if (global_scoring || summaryview->folder_item->prefs->scoring) {
1969 summaryview->important_score = prefs_common.important_score;
1970 if (summaryview->folder_item->prefs->important_score >
1971 summaryview->important_score)
1972 summaryview->important_score =
1973 summaryview->folder_item->prefs->important_score;
1976 if (summaryview->folder_item->threaded) {
1977 for (; mlist != NULL; mlist = mlist->next) {
1978 msginfo = (MsgInfo *)mlist->data;
1981 summary_set_header(summaryview, text, msginfo);
1983 /* search parent node for threading */
1984 if (msginfo->inreplyto && *msginfo->inreplyto) {
1985 parent = g_hash_table_lookup
1986 (msgid_table, msginfo->inreplyto);
1988 if (parent == NULL && msginfo->subject &&
1989 g_strncasecmp(msginfo->subject, "Re: ", 4) == 0) {
1990 parent = subject_table_lookup
1991 (subject_table, msginfo->subject);
1994 parentinfo = gtk_ctree_node_get_row_data(ctree, parent);
1995 if(!MSG_IS_IGNORE_THREAD(msginfo->flags) && parentinfo && MSG_IS_IGNORE_THREAD(parentinfo->flags)) {
1996 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
2000 node = gtk_ctree_insert_node
2001 (ctree, parent, NULL, text, 2,
2002 NULL, NULL, NULL, NULL, FALSE,
2003 parent ? TRUE : FALSE);
2004 GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
2005 summary_set_marks_func(ctree, node, summaryview);
2007 if (MSG_GET_COLORLABEL(msginfo->flags))
2008 summary_set_colorlabel_color(ctree, node, MSG_GET_COLORLABEL_VALUE(msginfo->flags));
2010 /* preserve previous node if the message is
2012 if (msginfo->msgid && *msginfo->msgid &&
2013 g_hash_table_lookup(msgid_table, msginfo->msgid)
2015 g_hash_table_insert(msgid_table,
2016 msginfo->msgid, node);
2017 if (msginfo->subject &&
2018 subject_table_lookup(subject_table,
2019 msginfo->subject) == NULL) {
2020 subject_table_insert(subject_table,
2021 msginfo->subject, node);
2024 cur_parent = parent;
2025 cur_msginfo = msginfo;
2026 while (cur_parent != NULL) {
2027 parentinfo = gtk_ctree_node_get_row_data(ctree, cur_parent);
2032 if (parentinfo->threadscore <
2033 cur_msginfo->threadscore) {
2035 parentinfo->threadscore =
2036 cur_msginfo->threadscore;
2038 s = itos(parentinfo->threadscore);
2039 gtk_ctree_node_set_text(ctree, cur_parent, S_COL_SCORE, s);
2044 cur_msginfo = parentinfo;
2045 if (cur_msginfo->inreplyto &&
2046 *cur_msginfo->inreplyto) {
2047 cur_parent = g_hash_table_lookup(msgid_table, cur_msginfo->inreplyto);
2049 if (cur_parent == NULL &&
2050 cur_msginfo->subject &&
2051 g_strncasecmp(cur_msginfo->subject,
2053 cur_parent = subject_table_lookup(subject_table, cur_msginfo->subject);
2058 /* complete the thread */
2059 summary_thread_build(summaryview, TRUE);
2061 for (; mlist != NULL; mlist = mlist->next) {
2062 msginfo = (MsgInfo *)mlist->data;
2064 summary_set_header(summaryview, text, msginfo);
2066 node = gtk_ctree_insert_node
2067 (ctree, NULL, NULL, text, 2,
2068 NULL, NULL, NULL, NULL, FALSE, FALSE);
2069 GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
2070 summary_set_marks_func(ctree, node, summaryview);
2072 if ( MSG_GET_COLORLABEL(msginfo->flags) )
2073 summary_set_colorlabel_color(ctree, node, MSG_GET_COLORLABEL_VALUE(msginfo->flags));
2075 if (msginfo->msgid && *msginfo->msgid &&
2076 g_hash_table_lookup(msgid_table, msginfo->msgid)
2078 g_hash_table_insert(msgid_table,
2079 msginfo->msgid, node);
2081 if (msginfo->subject &&
2082 subject_table_lookup(subject_table,
2083 msginfo->subject) == NULL)
2084 subject_table_insert(subject_table,
2085 msginfo->subject, node);
2089 if (prefs_common.enable_hscrollbar &&
2090 summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
2093 optimal_width = gtk_clist_optimal_column_width
2094 (GTK_CLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
2095 gtk_clist_set_column_width(GTK_CLIST(ctree),
2096 summaryview->col_pos[S_COL_SUBJECT],
2100 if (prefs_common.use_addr_book)
2101 end_address_completion();
2103 debug_print(_("done.\n"));
2104 STATUSBAR_POP(summaryview->mainwin);
2106 debug_print("\tmsgid hash table size = %d\n",
2107 g_hash_table_size(msgid_table));
2108 debug_print("\tsubject hash table size = %d\n",
2109 g_hash_table_size(subject_table));
2119 gint summary_write_cache(SummaryView *summaryview)
2121 struct wcachefp fps;
2122 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2123 gint ver = CACHE_VERSION;
2125 gchar *cachefile, *markfile;
2128 PrefsFolderItem *prefs;
2130 if (!summaryview->folder_item || !summaryview->folder_item->path)
2133 if (summaryview->folder_item->folder->update_mark != NULL)
2134 summaryview->folder_item->folder->update_mark(summaryview->folder_item->folder, summaryview->folder_item);
2136 cachefile = folder_item_get_cache_file(summaryview->folder_item);
2137 g_return_val_if_fail(cachefile != NULL, -1);
2138 if ((fps.cache_fp = fopen(cachefile, "w")) == NULL) {
2139 FILE_OP_ERROR(cachefile, "fopen");
2143 if (change_file_mode_rw(fps.cache_fp, cachefile) < 0)
2144 FILE_OP_ERROR(cachefile, "chmod");
2146 prefs = summaryview->folder_item->prefs;
2147 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2148 /* for cache file */
2149 filemode = prefs->folder_chmod;
2150 if (filemode & S_IRGRP) filemode |= S_IWGRP;
2151 if (filemode & S_IROTH) filemode |= S_IWOTH;
2153 fchmod(fileno(fps.cache_fp), filemode);
2155 chmod(cachefile, filemode);
2161 markfile = folder_item_get_mark_file(summaryview->folder_item);
2162 if ((fps.mark_fp = fopen(markfile, "w")) == NULL) {
2163 FILE_OP_ERROR(markfile, "fopen");
2164 fclose(fps.cache_fp);
2168 if (change_file_mode_rw(fps.mark_fp, markfile) < 0)
2169 FILE_OP_ERROR(markfile, "chmod");
2170 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2172 fchmod(fileno(fps.mark_fp), filemode);
2174 chmod(markfile, filemode);
2180 buf = g_strdup_printf(_("Writing summary cache (%s)..."),
2181 summaryview->folder_item->path);
2183 STATUSBAR_PUSH(summaryview->mainwin, buf);
2186 WRITE_CACHE_DATA_INT(ver, fps.cache_fp);
2188 WRITE_CACHE_DATA_INT(ver, fps.mark_fp);
2190 gtk_ctree_pre_recursive(ctree, NULL, summary_write_cache_func, &fps);
2192 for(cur = summaryview->killed_messages ; cur != NULL ;
2193 cur = g_slist_next(cur)) {
2194 MsgInfo *msginfo = (MsgInfo *) cur->data;
2196 procmsg_write_cache(msginfo, fps.cache_fp);
2197 procmsg_write_flags(msginfo, fps.mark_fp);
2200 fclose(fps.cache_fp);
2201 fclose(fps.mark_fp);
2203 debug_print(_("done.\n"));
2204 STATUSBAR_POP(summaryview->mainwin);
2209 static void summary_write_cache_func(GtkCTree *ctree, GtkCTreeNode *node,
2212 struct wcachefp *fps = data;
2213 MsgInfo *msginfo = gtk_ctree_node_get_row_data(ctree, node);
2215 if (msginfo == NULL) return;
2217 procmsg_write_cache(msginfo, fps->cache_fp);
2218 procmsg_write_flags(msginfo, fps->mark_fp);
2221 static void summary_set_header(SummaryView *summaryview, gchar *text[],
2224 static gchar date_modified[80];
2225 static gchar *to = NULL;
2226 static gchar *from_name = NULL;
2227 static gchar col_number[11];
2228 static gchar col_score[11];
2229 gint *col_pos = summaryview->col_pos;
2231 text[col_pos[S_COL_MARK]] = NULL;
2232 text[col_pos[S_COL_UNREAD]] = NULL;
2233 text[col_pos[S_COL_MIME]] = NULL;
2234 text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
2235 text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
2238 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->threadscore);
2240 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
2243 if (msginfo->date_t) {
2244 procheader_date_get_localtime(date_modified,
2245 sizeof(date_modified),
2247 text[col_pos[S_COL_DATE]] = date_modified;
2248 } else if (msginfo->date)
2249 text[col_pos[S_COL_DATE]] = msginfo->date;
2251 text[col_pos[S_COL_DATE]] = _("(No Date)");
2253 text[S_COL_FROM] = msginfo->fromname ? msginfo->fromname :
2255 if (prefs_common.swap_from && msginfo->from && msginfo->to &&
2256 !MSG_IS_NEWS(msginfo->flags)) {
2259 Xalloca(from, strlen(msginfo->from) + 1, return);
2260 strcpy(from, msginfo->from);
2261 extract_address(from);
2262 if (account_find_from_address(from)) {
2264 to = g_strconcat("-->", msginfo->to, NULL);
2265 text[col_pos[S_COL_FROM]] = to;
2269 if ((text[S_COL_FROM] != to) && prefs_common.use_addr_book &&
2274 Xalloca(from, strlen(msginfo->from) + 1, return);
2275 strcpy(from, msginfo->from);
2276 extract_address(from);
2278 count = complete_address(from);
2281 from = get_complete_address(1);
2282 from_name = procheader_get_fromname(from);
2284 text[S_COL_FROM] = from_name;
2289 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
2293 #define CHANGE_FLAGS(msginfo) \
2295 if (msginfo->folder->folder->change_flags != NULL) \
2296 msginfo->folder->folder->change_flags(msginfo->folder->folder, \
2301 static void summary_display_msg(SummaryView *summaryview, GtkCTreeNode *row,
2302 gboolean new_window)
2304 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2308 if (!new_window && summaryview->displayed == row) return;
2309 g_return_if_fail(row != NULL);
2311 if (summary_is_locked(summaryview)) return;
2312 summary_lock(summaryview);
2314 STATUSBAR_POP(summaryview->mainwin);
2317 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2319 filename = procmsg_get_message_file(msginfo);
2321 summary_unlock(summaryview);
2326 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2327 summaryview->newmsgs--;
2328 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2329 summaryview->unread--;
2330 if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
2331 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2332 CHANGE_FLAGS(msginfo);
2333 summary_set_row_marks(summaryview, row);
2334 gtk_clist_thaw(GTK_CLIST(ctree));
2335 summary_status_show(summaryview);
2339 MessageView *msgview;
2341 msgview = messageview_create_with_new_window();
2342 messageview_show(msgview, msginfo);
2344 MessageView *msgview;
2346 msgview = summaryview->messageview;
2348 summaryview->displayed = row;
2349 if (!summaryview->msg_is_toggled_on)
2350 summary_toggle_view(summaryview);
2351 messageview_show(msgview, msginfo);
2352 if (msgview->type == MVIEW_TEXT ||
2353 (msgview->type == MVIEW_MIME &&
2354 GTK_CLIST(msgview->mimeview->ctree)->row_list == NULL))
2355 gtk_widget_grab_focus(summaryview->ctree);
2357 gtkut_ctree_node_move_if_on_the_edge(ctree, row);
2360 if (GTK_WIDGET_VISIBLE(summaryview->headerwin->window))
2361 header_window_show(summaryview->headerwin, msginfo);
2363 summary_unlock(summaryview);
2366 void summary_redisplay_msg(SummaryView *summaryview)
2370 if (summaryview->displayed) {
2371 node = summaryview->displayed;
2372 summaryview->displayed = NULL;
2373 summary_display_msg(summaryview, node, FALSE);
2377 void summary_open_msg(SummaryView *summaryview)
2379 if (!summaryview->selected) return;
2381 summary_display_msg(summaryview, summaryview->selected, TRUE);
2384 void summary_view_source(SummaryView * summaryview)
2386 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2388 SourceWindow *srcwin;
2390 if (!summaryview->selected) return;
2392 srcwin = source_window_create();
2393 msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
2394 source_window_show_msg(srcwin, msginfo);
2395 source_window_show(srcwin);
2398 void summary_reedit(SummaryView *summaryview)
2402 if (!summaryview->selected) return;
2403 if (!summaryview->folder_item ||
2404 (summaryview->folder_item->stype != F_DRAFT &&
2405 summaryview->folder_item->stype != F_OUTBOX &&
2406 summaryview->folder_item->stype != F_QUEUE)) return;
2408 msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
2409 summaryview->selected);
2410 if (!msginfo) return;
2412 compose_reedit(msginfo);
2415 void summary_step(SummaryView *summaryview, GtkScrollType type)
2417 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2419 if (type == GTK_SCROLL_STEP_FORWARD) {
2421 node = gtkut_ctree_node_next(ctree, summaryview->selected);
2423 gtkut_ctree_expand_parent_all(ctree, node);
2426 gtk_signal_emit_by_name(GTK_OBJECT(ctree), "scroll_vertical",
2429 if (summaryview->msg_is_toggled_on)
2430 summary_display_msg(summaryview, summaryview->selected, FALSE);
2433 static void summary_toggle_view(SummaryView *summaryview)
2435 MainWindow *mainwin = summaryview->mainwin;
2436 union CompositeWin *cwin = &mainwin->win;
2437 GtkWidget *vpaned = NULL;
2438 GtkWidget *container = NULL;
2440 switch (mainwin->type) {
2442 vpaned = cwin->sep_none.vpaned;
2443 container = cwin->sep_none.hpaned;
2445 case SEPARATE_FOLDER:
2446 vpaned = cwin->sep_folder.vpaned;
2447 container = mainwin->vbox_body;
2449 case SEPARATE_MESSAGE:
2454 if (vpaned->parent != NULL) {
2455 summaryview->msg_is_toggled_on = FALSE;
2456 summaryview->displayed = NULL;
2457 gtk_widget_ref(vpaned);
2458 gtk_container_remove(GTK_CONTAINER(container), vpaned);
2459 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), container);
2460 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2461 GTK_ARROW_UP, GTK_SHADOW_OUT);
2463 summaryview->msg_is_toggled_on = TRUE;
2464 gtk_widget_reparent(GTK_WIDGET_PTR(summaryview), vpaned);
2465 gtk_container_add(GTK_CONTAINER(container), vpaned);
2466 gtk_widget_unref(vpaned);
2467 gtk_arrow_set(GTK_ARROW(summaryview->toggle_arrow),
2468 GTK_ARROW_DOWN, GTK_SHADOW_OUT);
2471 gtk_widget_grab_focus(summaryview->ctree);
2474 static gboolean summary_search_unread_recursive(GtkCTree *ctree,
2480 msginfo = gtk_ctree_node_get_row_data(ctree, node);
2481 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2483 node = GTK_CTREE_ROW(node)->children;
2485 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
2488 if (summary_search_unread_recursive(ctree, node) == TRUE)
2490 node = GTK_CTREE_ROW(node)->sibling;
2496 static gboolean summary_have_unread_children(SummaryView *summaryview,
2499 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2501 if (!node) return FALSE;
2503 node = GTK_CTREE_ROW(node)->children;
2506 if (summary_search_unread_recursive(ctree, node) == TRUE)
2508 node = GTK_CTREE_ROW(node)->sibling;
2514 static void summary_set_row_marks(SummaryView *summaryview, GtkCTreeNode *row)
2516 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2517 GtkStyle *style = NULL;
2520 gint *col_pos = summaryview->col_pos;
2522 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2523 if (!msginfo) return;
2525 flags = msginfo->flags;
2527 gtk_ctree_node_set_foreground(ctree, row, NULL);
2529 /* set new/unread column */
2530 if (MSG_IS_IGNORE_THREAD(flags)) {
2531 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2532 ignorethreadxpm, ignorethreadxpmmask);
2533 } else if (MSG_IS_NEW(flags)) {
2534 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2535 newxpm, newxpmmask);
2536 } else if (MSG_IS_UNREAD(flags)) {
2537 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2538 unreadxpm, unreadxpmmask);
2539 } else if (MSG_IS_REPLIED(flags)) {
2540 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2541 repliedxpm, repliedxpmmask);
2542 } else if (MSG_IS_FORWARDED(flags)) {
2543 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_UNREAD],
2544 forwardedxpm, forwardedxpmmask);
2546 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_UNREAD],
2550 if (prefs_common.bold_unread &&
2551 ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
2552 (!GTK_CTREE_ROW(row)->expanded &&
2553 GTK_CTREE_ROW(row)->children &&
2554 summary_have_unread_children(summaryview, row))))
2557 /* set mark column */
2558 if (MSG_IS_DELETED(flags)) {
2559 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2560 deletedxpm, deletedxpmmask);
2562 style = bold_deleted_style;
2564 style = small_deleted_style;
2566 gtk_ctree_node_set_foreground
2567 (ctree, row, &summaryview->color_dim);
2568 } else if (MSG_IS_MARKED(flags)) {
2569 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MARK],
2570 markxpm, markxpmmask);
2571 } else if (MSG_IS_MOVE(flags)) {
2572 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "o");
2574 style = bold_marked_style;
2576 style = small_marked_style;
2578 gtk_ctree_node_set_foreground
2579 (ctree, row, &summaryview->color_marked);
2580 } else if (MSG_IS_COPY(flags)) {
2581 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "O");
2583 style = bold_marked_style;
2585 style = small_marked_style;
2587 gtk_ctree_node_set_foreground
2588 (ctree, row, &summaryview->color_marked);
2590 else if ((global_scoring ||
2591 summaryview->folder_item->prefs->scoring) &&
2592 (msginfo->score >= summaryview->important_score) &&
2593 (MSG_IS_MARKED(msginfo->flags) || MSG_IS_MOVE(msginfo->flags) || MSG_IS_COPY(msginfo->flags))) {
2594 gtk_ctree_node_set_text(ctree, row, S_COL_MARK, "!");
2595 gtk_ctree_node_set_foreground(ctree, row,
2596 &summaryview->color_important);
2598 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MARK], NULL);
2601 if (MSG_IS_MIME(flags)) {
2602 gtk_ctree_node_set_pixmap(ctree, row, col_pos[S_COL_MIME],
2603 clipxpm, clipxpmmask);
2605 gtk_ctree_node_set_text(ctree, row, col_pos[S_COL_MIME], NULL);
2608 style = small_style;
2610 gtk_ctree_node_set_row_style(ctree, row, style);
2612 if (MSG_GET_COLORLABEL(flags))
2613 summary_set_colorlabel_color(ctree, row,
2614 MSG_GET_COLORLABEL_VALUE(flags));
2617 void summary_set_marks_selected(SummaryView *summaryview)
2619 summary_set_row_marks(summaryview, summaryview->selected);
2622 static void summary_mark_row(SummaryView *summaryview, GtkCTreeNode *row)
2624 gboolean changed = FALSE;
2625 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2628 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2629 if (MSG_IS_DELETED(msginfo->flags))
2630 summaryview->deleted--;
2631 if (MSG_IS_MOVE(msginfo->flags)) {
2632 summaryview->moved--;
2635 if (MSG_IS_COPY(msginfo->flags)) {
2636 summaryview->copied--;
2639 if (changed && !prefs_common.immediate_exec) {
2640 msginfo->to_folder->op_count--;
2641 if (msginfo->to_folder->op_count == 0)
2642 folderview_update_item(msginfo->to_folder, 0);
2644 msginfo->to_folder = NULL;
2645 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2646 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2647 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2648 CHANGE_FLAGS(msginfo);
2649 summary_set_row_marks(summaryview, row);
2650 debug_print(_("Message %d is marked\n"), msginfo->msgnum);
2653 void summary_mark(SummaryView *summaryview)
2655 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2658 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2659 summary_mark_row(summaryview, GTK_CTREE_NODE(cur->data));
2661 /* summary_step(summaryview, GTK_SCROLL_STEP_FORWARD); */
2662 summary_status_show(summaryview);
2665 static void summary_mark_row_as_read(SummaryView *summaryview,
2668 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2671 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2672 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2673 summaryview->newmsgs--;
2674 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
2675 summaryview->unread--;
2676 if (MSG_IS_NEW(msginfo->flags) ||
2677 MSG_IS_UNREAD(msginfo->flags)) {
2678 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_NEW | MSG_UNREAD);
2679 CHANGE_FLAGS(msginfo);
2680 summary_set_row_marks(summaryview, row);
2681 debug_print(_("Message %d is marked as read\n"),
2686 void summary_mark_as_read(SummaryView *summaryview)
2688 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2691 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2692 summary_mark_row_as_read(summaryview,
2693 GTK_CTREE_NODE(cur->data));
2695 summary_status_show(summaryview);
2698 void summary_mark_all_read(SummaryView *summaryview)
2700 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2701 GtkCList *clist = GTK_CLIST(summaryview->ctree);
2704 gtk_clist_freeze(clist);
2705 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2706 node = gtkut_ctree_node_next(ctree, node))
2707 summary_mark_row_as_read(summaryview, node);
2708 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list); node != NULL;
2709 node = gtkut_ctree_node_next(ctree, node)) {
2710 if (!GTK_CTREE_ROW(node)->expanded)
2711 summary_set_row_marks(summaryview, node);
2713 gtk_clist_thaw(clist);
2715 summary_status_show(summaryview);
2718 static void summary_mark_row_as_unread(SummaryView *summaryview,
2721 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2724 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2725 if (MSG_IS_DELETED(msginfo->flags)) {
2726 msginfo->to_folder = NULL;
2727 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2728 summaryview->deleted--;
2730 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_REPLIED | MSG_FORWARDED);
2731 if (!MSG_IS_UNREAD(msginfo->flags)) {
2732 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_UNREAD);
2733 summaryview->unread++;
2734 debug_print(_("Message %d is marked as unread\n"),
2738 CHANGE_FLAGS(msginfo);
2740 summary_set_row_marks(summaryview, row);
2743 void summary_mark_as_unread(SummaryView *summaryview)
2745 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2748 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next)
2749 summary_mark_row_as_unread(summaryview,
2750 GTK_CTREE_NODE(cur->data));
2752 summary_status_show(summaryview);
2755 static void summary_delete_row(SummaryView *summaryview, GtkCTreeNode *row)
2757 gboolean changed = FALSE;
2758 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2761 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2763 if (MSG_IS_DELETED(msginfo->flags)) return;
2765 if (MSG_IS_MOVE(msginfo->flags)) {
2766 summaryview->moved--;
2769 if (MSG_IS_COPY(msginfo->flags)) {
2770 summaryview->copied--;
2773 if (changed && !prefs_common.immediate_exec) {
2774 msginfo->to_folder->op_count--;
2775 if (msginfo->to_folder->op_count == 0)
2776 folderview_update_item(msginfo->to_folder, 0);
2778 msginfo->to_folder = NULL;
2779 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
2780 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2781 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
2782 CHANGE_FLAGS(msginfo);
2783 summaryview->deleted++;
2785 if (!prefs_common.immediate_exec &&
2786 summaryview->folder_item->stype != F_TRASH)
2787 summary_set_row_marks(summaryview, row);
2789 debug_print(_("Message %s/%d is set to delete\n"),
2790 msginfo->folder->path, msginfo->msgnum);
2793 void summary_delete(SummaryView *summaryview)
2795 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2796 FolderItem *item = summaryview->folder_item;
2798 GtkCTreeNode *sel_last = NULL;
2801 if (!item || item->folder->type == F_NEWS) return;
2803 if (summary_is_locked(summaryview)) return;
2805 /* if current folder is trash, ask for confirmation */
2806 if (item->stype == F_TRASH) {
2809 aval = alertpanel(_("Delete message(s)"),
2810 _("Do you really want to delete message(s) from the trash?"),
2811 _("Yes"), _("No"), NULL);
2812 if (aval != G_ALERTDEFAULT) return;
2815 /* next code sets current row focus right. We need to find a row
2816 * that is not deleted. */
2817 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
2818 sel_last = GTK_CTREE_NODE(cur->data);
2819 summary_delete_row(summaryview, sel_last);
2822 node = summary_find_next_msg(summaryview, sel_last);
2824 node = summary_find_prev_msg(summaryview, sel_last);
2825 if (node == gtkut_ctree_node_next(ctree, sel_last))
2826 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2827 else if (node == GTK_CTREE_NODE_PREV(sel_last))
2828 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
2832 summaryview->msg_is_toggled_on);
2834 if (prefs_common.immediate_exec || item->stype == F_TRASH)
2835 summary_execute(summaryview);
2837 summary_status_show(summaryview);
2840 void summary_delete_duplicated(SummaryView *summaryview)
2842 if (!summaryview->folder_item ||
2843 summaryview->folder_item->folder->type == F_NEWS) return;
2844 if (summaryview->folder_item->stype == F_TRASH) return;
2846 main_window_cursor_wait(summaryview->mainwin);
2847 debug_print(_("Deleting duplicated messages..."));
2848 STATUSBAR_PUSH(summaryview->mainwin,
2849 _("Deleting duplicated messages..."));
2851 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
2852 GTK_CTREE_FUNC(summary_delete_duplicated_func),
2855 if (prefs_common.immediate_exec)
2856 summary_execute(summaryview);
2858 summary_status_show(summaryview);
2860 debug_print(_("done.\n"));
2861 STATUSBAR_POP(summaryview->mainwin);
2862 main_window_cursor_normal(summaryview->mainwin);
2865 static void summary_delete_duplicated_func(GtkCTree *ctree, GtkCTreeNode *node,
2866 SummaryView *summaryview)
2868 GtkCTreeNode *found;
2869 MsgInfo *msginfo = GTK_CTREE_ROW(node)->row.data;
2871 if (!msginfo->msgid || !*msginfo->msgid) return;
2873 found = g_hash_table_lookup(summaryview->msgid_table, msginfo->msgid);
2875 if (found && found != node)
2876 summary_delete_row(summaryview, node);
2879 static void summary_unmark_row(SummaryView *summaryview, GtkCTreeNode *row)
2881 gboolean changed = FALSE;
2882 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2885 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2886 if (MSG_IS_DELETED(msginfo->flags))
2887 summaryview->deleted--;
2888 if (MSG_IS_MOVE(msginfo->flags)) {
2889 summaryview->moved--;
2892 if (MSG_IS_COPY(msginfo->flags)) {
2893 summaryview->copied--;
2896 if (changed && !prefs_common.immediate_exec) {
2897 msginfo->to_folder->op_count--;
2898 if (msginfo->to_folder->op_count == 0)
2899 folderview_update_item(msginfo->to_folder, 0);
2901 msginfo->to_folder = NULL;
2902 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2903 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE | MSG_COPY);
2904 CHANGE_FLAGS(msginfo);
2905 summary_set_row_marks(summaryview, row);
2907 debug_print(_("Message %s/%d is unmarked\n"),
2908 msginfo->folder->path, msginfo->msgnum);
2911 void summary_unmark(SummaryView *summaryview)
2913 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2916 for (cur = GTK_CLIST(ctree)->selection; cur != NULL;
2918 summary_unmark_row(summaryview, GTK_CTREE_NODE(cur->data));
2920 summary_status_show(summaryview);
2923 static void summary_move_row_to(SummaryView *summaryview, GtkCTreeNode *row,
2924 FolderItem *to_folder)
2926 gboolean changed = FALSE;
2927 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
2930 g_return_if_fail(to_folder != NULL);
2932 msginfo = gtk_ctree_node_get_row_data(ctree, row);
2933 if (MSG_IS_MOVE(msginfo->flags)) {
2934 if (!prefs_common.immediate_exec) {
2935 msginfo->to_folder->op_count--;
2936 if (msginfo->to_folder->op_count == 0) {
2937 folderview_update_item(msginfo->to_folder, 0);
2942 msginfo->to_folder = to_folder;
2943 if (MSG_IS_DELETED(msginfo->flags))
2944 summaryview->deleted--;
2945 if (MSG_IS_COPY(msginfo->flags)) {
2946 summaryview->copied--;
2947 if (!prefs_common.immediate_exec)
2948 msginfo->to_folder->op_count--;
2950 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
2951 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
2952 if (!MSG_IS_MOVE(msginfo->flags)) {
2953 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
2954 summaryview->moved++;
2957 if (!prefs_common.immediate_exec) {
2958 summary_set_row_marks(summaryview, row);
2960 msginfo->to_folder->op_count++;
2961 if (msginfo->to_folder->op_count == 1)
2962 folderview_update_item(msginfo->to_folder, 0);
2966 debug_print(_("Message %d is set to move to %s\n"),
2967 msginfo->msgnum, to_folder->path);
2970 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
2974 if (!to_folder) return;
2975 if (!summaryview->folder_item ||
2976 summaryview->folder_item->folder->type == F_NEWS) return;
2978 if (summary_is_locked(summaryview)) return;
2980 if (summaryview->folder_item == to_folder) {
2981 alertpanel_notice(_("Destination is same as current folder."));
2985 for (cur = GTK_CLIST(summaryview->ctree)->selection;
2986 cur != NULL; cur = cur->next)
2988 (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
2990 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
2992 if (prefs_common.immediate_exec)
2993 summary_execute(summaryview);
2995 summary_status_show(summaryview);
2997 folderview_update_item(to_folder, 0);
3001 void summary_move_to(SummaryView *summaryview)
3003 FolderItem *to_folder;
3005 if (!summaryview->folder_item ||
3006 summaryview->folder_item->folder->type == F_NEWS) return;
3008 to_folder = foldersel_folder_sel(NULL, NULL);
3009 summary_move_selected_to(summaryview, to_folder);
3012 static void summary_copy_row_to(SummaryView *summaryview, GtkCTreeNode *row,
3013 FolderItem *to_folder)
3015 gboolean changed = FALSE;
3016 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3019 g_return_if_fail(to_folder != NULL);
3021 msginfo = gtk_ctree_node_get_row_data(ctree, row);
3022 if (MSG_IS_COPY(msginfo->flags)) {
3023 if (!prefs_common.immediate_exec) {
3024 msginfo->to_folder->op_count--;
3025 if (msginfo->to_folder->op_count == 0) {
3026 folderview_update_item(msginfo->to_folder, 0);
3031 msginfo->to_folder = to_folder;
3032 if (MSG_IS_DELETED(msginfo->flags))
3033 summaryview->deleted--;
3034 if (MSG_IS_MOVE(msginfo->flags)) {
3035 summaryview->moved--;
3036 if (!prefs_common.immediate_exec)
3037 msginfo->to_folder->op_count--;
3039 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED | MSG_DELETED);
3040 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_MOVE);
3041 if (!MSG_IS_COPY(msginfo->flags)) {
3042 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3043 summaryview->copied++;
3046 if (!prefs_common.immediate_exec) {
3047 summary_set_row_marks(summaryview, row);
3049 msginfo->to_folder->op_count++;
3050 if (msginfo->to_folder->op_count == 1)
3051 folderview_update_item(msginfo->to_folder, 0);
3055 debug_print(_("Message %d is set to copy to %s\n"),
3056 msginfo->msgnum, to_folder->path);
3059 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
3063 if (!to_folder) return;
3064 if (!summaryview->folder_item ||
3065 summaryview->folder_item->folder->type == F_NEWS) return;
3067 if (summary_is_locked(summaryview)) return;
3069 if (summaryview->folder_item == to_folder) {
3071 (_("Destination to copy is same as current folder."));
3075 for (cur = GTK_CLIST(summaryview->ctree)->selection;
3076 cur != NULL; cur = cur->next)
3078 (summaryview, GTK_CTREE_NODE(cur->data), to_folder);
3080 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
3082 if (prefs_common.immediate_exec)
3083 summary_execute(summaryview);
3085 summary_status_show(summaryview);
3087 folderview_update_item(to_folder, 0);
3091 void summary_copy_to(SummaryView *summaryview)
3093 FolderItem *to_folder;
3095 if (!summaryview->folder_item ||
3096 summaryview->folder_item->folder->type == F_NEWS) return;
3098 to_folder = foldersel_folder_sel(NULL, NULL);
3099 summary_copy_selected_to(summaryview, to_folder);
3102 void summary_add_address(SummaryView *summaryview)
3107 msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
3108 summaryview->selected);
3109 if (!msginfo) return;
3111 Xstrdup_a(from, msginfo->from, return);
3112 eliminate_address_comment(from);
3113 extract_address(from);
3114 addressbook_add_contact(msginfo->fromname, from, NULL);
3117 void summary_select_all(SummaryView *summaryview)
3119 if (summaryview->messages >= 500) {
3120 STATUSBAR_PUSH(summaryview->mainwin,
3121 _("Selecting all messages..."));
3122 main_window_cursor_wait(summaryview->mainwin);
3125 gtk_clist_select_all(GTK_CLIST(summaryview->ctree));
3127 if (summaryview->messages >= 500) {
3128 STATUSBAR_POP(summaryview->mainwin);
3129 main_window_cursor_normal(summaryview->mainwin);
3133 void summary_unselect_all(SummaryView *summaryview)
3135 gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
3138 void summary_save_as(SummaryView *summaryview)
3140 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3144 if (!summaryview->selected) return;
3145 msginfo = gtk_ctree_node_get_row_data(ctree, summaryview->selected);
3146 if (!msginfo) return;
3148 dest = filesel_select_file(_("Save as"), NULL);
3150 if (is_file_exist(dest)) {
3153 aval = alertpanel(_("Overwrite"),
3154 _("Overwrite existing file?"),
3155 _("OK"), _("Cancel"), NULL);
3156 if (G_ALERTDEFAULT != aval) return;
3159 src = procmsg_get_message_file(msginfo);
3160 copy_file(src, dest);
3164 void summary_print(SummaryView *summaryview)
3166 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3167 GtkCList *clist = GTK_CLIST(summaryview->ctree);
3173 if (clist->selection == NULL) return;
3175 cmdline = input_dialog(_("Print"),
3176 _("Enter the print command line:\n"
3177 "(`%s' will be replaced with file name)"),
3178 prefs_common.print_cmd);
3179 if (!cmdline) return;
3180 if (!(p = strchr(cmdline, '%')) || *(p + 1) != 's' ||
3181 strchr(p + 2, '%')) {
3182 alertpanel_error(_("Print command line is invalid:\n`%s'"),
3188 for (cur = clist->selection; cur != NULL; cur = cur->next) {
3189 msginfo = gtk_ctree_node_get_row_data
3190 (ctree, GTK_CTREE_NODE(cur->data));
3191 if (msginfo) procmsg_print_message(msginfo, cmdline);
3197 gboolean summary_execute(SummaryView *summaryview)
3199 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3200 GtkCList *clist = GTK_CLIST(summaryview->ctree);
3201 GtkCTreeNode *node, *next;
3203 if (!summaryview->folder_item ||
3204 summaryview->folder_item->folder->type == F_NEWS) return FALSE;
3206 if (summary_is_locked(summaryview)) return FALSE;
3207 summary_lock(summaryview);
3209 gtk_clist_freeze(clist);
3211 if (summaryview->folder_item->threaded)
3212 summary_unthread_for_exec(summaryview);
3214 summary_execute_move(summaryview);
3215 summary_execute_copy(summaryview);
3216 summary_execute_delete(summaryview);
3218 node = GTK_CTREE_NODE(clist->row_list);
3219 while (node != NULL) {
3220 next = gtkut_ctree_node_next(ctree, node);
3221 if (gtk_ctree_node_get_row_data(ctree, node) == NULL) {
3222 if (node == summaryview->displayed) {
3223 messageview_clear(summaryview->messageview);
3224 summaryview->displayed = NULL;
3226 if (GTK_CTREE_ROW(node)->children != NULL)
3227 g_warning("summary_execute(): children != NULL\n");
3229 gtk_ctree_remove_node(ctree, node);
3234 if (summaryview->folder_item->threaded)
3235 summary_thread_build(summaryview, FALSE);
3237 summaryview->selected = clist->selection ?
3238 GTK_CTREE_NODE(clist->selection->data) : NULL;
3240 if (!GTK_CLIST(summaryview->ctree)->row_list) {
3241 menu_set_insensitive_all
3242 (GTK_MENU_SHELL(summaryview->popupmenu));
3243 gtk_widget_grab_focus(summaryview->folderview->ctree);
3245 gtk_widget_grab_focus(summaryview->ctree);
3247 summary_update_status(summaryview);
3248 summary_status_show(summaryview);
3250 summary_write_cache(summaryview);
3252 gtk_ctree_node_moveto(ctree, summaryview->selected, -1, 0.5, 0);
3254 gtk_clist_thaw(clist);
3256 summary_unlock(summaryview);
3260 static void summary_execute_move(SummaryView *summaryview)
3262 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3265 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3267 /* search moving messages and execute */
3268 gtk_ctree_pre_recursive(ctree, NULL, summary_execute_move_func,
3271 if (summaryview->mlist) {
3272 procmsg_move_messages(summaryview->mlist);
3274 folder_item_scan_foreach(summaryview->folder_table);
3275 folderview_update_item_foreach(summaryview->folder_table);
3277 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3278 procmsg_msginfo_free((MsgInfo *)cur->data);
3279 g_slist_free(summaryview->mlist);
3280 summaryview->mlist = NULL;
3283 g_hash_table_destroy(summaryview->folder_table);
3284 summaryview->folder_table = NULL;
3287 static void summary_execute_move_func(GtkCTree *ctree, GtkCTreeNode *node,
3290 SummaryView *summaryview = data;
3293 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3295 if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
3296 g_hash_table_insert(summaryview->folder_table,
3297 msginfo->to_folder, GINT_TO_POINTER(1));
3299 summaryview->mlist =
3300 g_slist_append(summaryview->mlist, msginfo);
3301 gtk_ctree_node_set_row_data(ctree, node, NULL);
3303 if (msginfo->msgid && *msginfo->msgid &&
3304 node == g_hash_table_lookup(summaryview->msgid_table,
3306 g_hash_table_remove(summaryview->msgid_table,
3311 static void summary_execute_copy(SummaryView *summaryview)
3313 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3315 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3317 /* search copying messages and execute */
3318 gtk_ctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
3321 if (summaryview->mlist) {
3322 procmsg_copy_messages(summaryview->mlist);
3324 folder_item_scan_foreach(summaryview->folder_table);
3325 folderview_update_item_foreach(summaryview->folder_table);
3327 g_slist_free(summaryview->mlist);
3328 summaryview->mlist = NULL;
3331 g_hash_table_destroy(summaryview->folder_table);
3332 summaryview->folder_table = NULL;
3335 static void summary_execute_copy_func(GtkCTree *ctree, GtkCTreeNode *node,
3338 SummaryView *summaryview = data;
3341 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3343 if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
3344 g_hash_table_insert(summaryview->folder_table,
3345 msginfo->to_folder, GINT_TO_POINTER(1));
3347 summaryview->mlist =
3348 g_slist_append(summaryview->mlist, msginfo);
3350 MSG_UNSET_TMP_FLAGS(msginfo->flags, MSG_COPY);
3351 summary_set_row_marks(summaryview, node);
3355 static void summary_execute_delete(SummaryView *summaryview)
3357 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3361 trash = summaryview->folder_item->folder->trash;
3362 if (summaryview->folder_item->folder->type == F_MH) {
3363 g_return_if_fail(trash != NULL);
3366 /* search deleting messages and execute */
3367 gtk_ctree_pre_recursive
3368 (ctree, NULL, summary_execute_delete_func, summaryview);
3370 if (!summaryview->mlist) return;
3372 if (summaryview->folder_item != trash)
3373 folder_item_move_msgs_with_dest(trash, summaryview->mlist);
3375 folder_item_remove_msgs(trash, summaryview->mlist);
3377 for (cur = summaryview->mlist; cur != NULL; cur = cur->next)
3378 procmsg_msginfo_free((MsgInfo *)cur->data);
3380 g_slist_free(summaryview->mlist);
3381 summaryview->mlist = NULL;
3383 if (summaryview->folder_item != trash) {
3384 folder_item_scan(trash);
3385 folderview_update_item(trash, FALSE);
3389 static void summary_execute_delete_func(GtkCTree *ctree, GtkCTreeNode *node,
3392 SummaryView *summaryview = data;
3395 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3397 if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
3398 summaryview->mlist =
3399 g_slist_append(summaryview->mlist, msginfo);
3400 gtk_ctree_node_set_row_data(ctree, node, NULL);
3402 if (msginfo->msgid && *msginfo->msgid &&
3403 node == g_hash_table_lookup(summaryview->msgid_table,
3405 g_hash_table_remove(summaryview->msgid_table,
3408 if (msginfo->subject &&
3409 node == subject_table_lookup(summaryview->subject_table,
3410 msginfo->subject)) {
3411 gchar *s = msginfo->subject + (g_strncasecmp(msginfo->subject, "Re: ", 4) == 0 ? 4 : 0);
3412 g_hash_table_remove(summaryview->subject_table, s);
3417 /* thread functions */
3419 void summary_thread_build(SummaryView *summaryview, gboolean init)
3421 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3424 GtkCTreeNode *parent;
3427 summary_lock(summaryview);
3429 debug_print(_("Building threads..."));
3430 STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
3431 main_window_cursor_wait(summaryview->mainwin);
3433 gtk_signal_handler_block_by_func(GTK_OBJECT(ctree),
3434 summary_tree_expanded, summaryview);
3435 gtk_clist_freeze(GTK_CLIST(ctree));
3437 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3439 next = GTK_CTREE_ROW(node)->sibling;
3441 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3443 /* alfons - claws seems to prefer subject threading before
3444 * inreplyto threading. we should look more deeply in this,
3445 * because inreplyto should have precedence... */
3446 if (msginfo && msginfo->inreplyto) {
3447 parent = g_hash_table_lookup(summaryview->msgid_table,
3448 msginfo->inreplyto);
3449 if (parent && parent != node) {
3450 gtk_ctree_move(ctree, node, parent, NULL);
3451 gtk_ctree_expand(ctree, node);
3454 else if (msginfo && msginfo->subject) {
3455 parent = g_hash_table_lookup
3456 (summaryview->subject_table, msginfo->subject);
3457 if (parent && parent != node) {
3458 gtk_ctree_move(ctree, node, parent, NULL);
3459 gtk_ctree_expand(ctree, node);
3466 node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3468 /* for optimization */
3470 if (prefs_common.expand_thread) {
3472 next = GTK_CTREE_ROW(node)->sibling;
3473 if (GTK_CTREE_ROW(node)->children)
3474 gtk_ctree_expand(ctree, node);
3477 } else if (prefs_common.bold_unread) {
3479 next = GTK_CTREE_ROW(node)->sibling;
3480 if (GTK_CTREE_ROW(node)->children)
3481 summary_set_row_marks
3482 (summaryview, node);
3488 next = GTK_CTREE_NODE_NEXT(node);
3489 if (prefs_common.expand_thread)
3490 gtk_ctree_expand(ctree, node);
3491 if (prefs_common.bold_unread &&
3492 GTK_CTREE_ROW(node)->children)
3493 summary_set_row_marks(summaryview, node);
3498 gtk_clist_thaw(GTK_CLIST(ctree));
3499 gtk_signal_handler_unblock_by_func(GTK_OBJECT(ctree),
3500 summary_tree_expanded, summaryview);
3502 debug_print(_("done.\n"));
3503 STATUSBAR_POP(summaryview->mainwin);
3504 main_window_cursor_normal(summaryview->mainwin);
3506 summary_unlock(summaryview);
3509 void summary_unthread(SummaryView *summaryview)
3511 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3513 GtkCTreeNode *child;
3514 GtkCTreeNode *sibling;
3515 GtkCTreeNode *next_child;
3517 summary_lock(summaryview);
3519 debug_print(_("Unthreading..."));
3520 STATUSBAR_PUSH(summaryview->mainwin, _("Unthreading..."));
3521 main_window_cursor_wait(summaryview->mainwin);
3523 gtk_signal_handler_block_by_func(GTK_OBJECT(ctree),
3524 summary_tree_collapsed, summaryview);
3525 gtk_clist_freeze(GTK_CLIST(ctree));
3527 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3528 node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
3529 child = GTK_CTREE_ROW(node)->children;
3530 sibling = GTK_CTREE_ROW(node)->sibling;
3532 while (child != NULL) {
3533 next_child = GTK_CTREE_ROW(child)->sibling;
3534 gtk_ctree_move(ctree, child, NULL, sibling);
3539 gtk_clist_thaw(GTK_CLIST(ctree));
3540 gtk_signal_handler_unblock_by_func(GTK_OBJECT(ctree),
3541 summary_tree_collapsed, summaryview);
3543 debug_print(_("done.\n"));
3544 STATUSBAR_POP(summaryview->mainwin);
3545 main_window_cursor_normal(summaryview->mainwin);
3547 summary_unlock(summaryview);
3550 static void summary_unthread_for_exec(SummaryView *summaryview)
3553 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3555 summary_lock(summaryview);
3557 debug_print(_("Unthreading for execution..."));
3559 gtk_clist_freeze(GTK_CLIST(ctree));
3561 for (node = GTK_CTREE_NODE(GTK_CLIST(ctree)->row_list);
3562 node != NULL; node = GTK_CTREE_NODE_NEXT(node)) {
3563 summary_unthread_for_exec_func(ctree, node, NULL);
3566 gtk_clist_thaw(GTK_CLIST(ctree));
3568 debug_print(_("done.\n"));
3570 summary_unlock(summaryview);
3573 static void summary_unthread_for_exec_func(GtkCTree *ctree, GtkCTreeNode *node,
3577 GtkCTreeNode *top_parent;
3578 GtkCTreeNode *child;
3579 GtkCTreeNode *sibling;
3581 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3584 (!MSG_IS_MOVE(msginfo->flags) &&
3585 !MSG_IS_DELETED(msginfo->flags)))
3587 child = GTK_CTREE_ROW(node)->children;
3590 for (top_parent = node;
3591 GTK_CTREE_ROW(top_parent)->parent != NULL;
3592 top_parent = GTK_CTREE_ROW(top_parent)->parent)
3594 sibling = GTK_CTREE_ROW(top_parent)->sibling;
3596 while (child != NULL) {
3597 GtkCTreeNode *next_child;
3599 next_child = GTK_CTREE_ROW(child)->sibling;
3600 gtk_ctree_move(ctree, child, NULL, sibling);
3605 void summary_filter(SummaryView *summaryview)
3607 if (!prefs_common.fltlist) return;
3609 summary_lock(summaryview);
3611 debug_print(_("filtering..."));
3612 STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
3613 main_window_cursor_wait(summaryview->mainwin);
3615 gtk_clist_freeze(GTK_CLIST(summaryview->ctree));
3617 if (prefs_filtering == NULL) {
3618 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3619 GTK_CTREE_FUNC(summary_filter_func),
3622 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3624 if (prefs_common.immediate_exec)
3625 summary_execute(summaryview);
3627 summary_status_show(summaryview);
3630 summaryview->folder_table = g_hash_table_new(NULL, NULL);
3632 gtk_ctree_pre_recursive(GTK_CTREE(summaryview->ctree), NULL,
3633 GTK_CTREE_FUNC(summary_filter_func),
3636 gtk_clist_thaw(GTK_CLIST(summaryview->ctree));
3638 folder_item_scan_foreach(summaryview->folder_table);
3639 folderview_update_item_foreach(summaryview->folder_table);
3641 g_hash_table_destroy(summaryview->folder_table);
3642 summaryview->folder_table = NULL;
3644 summary_show(summaryview, summaryview->folder_item, FALSE);
3647 debug_print(_("done.\n"));
3648 STATUSBAR_POP(summaryview->mainwin);
3649 main_window_cursor_normal(summaryview->mainwin);
3651 summary_unlock(summaryview);
3654 static void summary_filter_func(GtkCTree *ctree, GtkCTreeNode *node,
3657 MsgInfo *msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
3658 SummaryView *summaryview = data;
3662 if (prefs_filtering == NULL) {
3664 file = procmsg_get_message_file_path(msginfo);
3665 dest = filter_get_dest_folder(prefs_common.fltlist, file);
3668 if (dest && strcmp2(dest->path, FILTER_NOT_RECEIVE) != 0 &&
3669 summaryview->folder_item != dest)
3670 summary_move_row_to(summaryview, node, dest);
3673 filter_msginfo_move_or_delete(prefs_filtering, msginfo,
3674 summaryview->folder_table);
3679 #define N_COLOR_LABELS colorlabel_get_color_count()
3681 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
3684 guint color = GPOINTER_TO_UINT(data);
3685 SummaryView *summaryview;
3687 summaryview = gtk_object_get_data(GTK_OBJECT(widget), "summaryview");
3688 g_return_if_fail(summaryview != NULL);
3690 /* "dont_toggle" state set? */
3691 if (gtk_object_get_data(GTK_OBJECT(summaryview->colorlabel_menu),
3695 summary_set_colorlabel(summaryview, color, NULL);
3698 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
3699 * for the messsage; not the color index */
3700 void summary_set_colorlabel_color(GtkCTree *ctree, GtkCTreeNode *node,
3704 GtkStyle *style, *prev_style, *ctree_style;
3708 msginfo = gtk_ctree_node_get_row_data(ctree, node);
3709 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_CLABEL_FLAG_MASK);
3710 MSG_SET_COLORLABEL_VALUE(msginfo->flags, labelcolor);
3712 color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
3713 ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
3714 prev_style = gtk_ctree_node_get_row_style(ctree, node);
3716 if (color_index < 0 || color_index >= N_COLOR_LABELS) {
3717 if (!prev_style) return;
3718 style = gtk_style_copy(prev_style);
3719 color = ctree_style->fg[GTK_STATE_NORMAL];
3720 style->fg[GTK_STATE_NORMAL] = color;
3721 color = ctree_style->fg[GTK_STATE_SELECTED];
3722 style->fg[GTK_STATE_SELECTED] = color;
3725 style = gtk_style_copy(prev_style);
3727 style = gtk_style_copy(ctree_style);
3728 color = colorlabel_get_color(color_index);
3729 style->fg[GTK_STATE_NORMAL] = color;
3730 /* get the average of label color and selected fg color
3732 style->fg[GTK_STATE_SELECTED].red = (color.red + ctree_style->fg[GTK_STATE_SELECTED].red ) / 2;
3733 style->fg[GTK_STATE_SELECTED].green = (color.green + ctree_style->fg[GTK_STATE_SELECTED].green) / 2;
3734 style->fg[GTK_STATE_SELECTED].blue = (color.blue + ctree_style->fg[GTK_STATE_SELECTED].blue ) / 2;
3737 gtk_ctree_node_set_row_style(ctree, node, style);
3740 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
3743 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
3744 GtkCList *clist = GTK_CLIST(summaryview->ctree);
3747 for (cur = clist->selection; cur != NULL; cur = cur->next)
3748 summary_set_colorlabel_color(ctree, GTK_CTREE_NODE(cur->data),
3752 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
3755 SummaryView *summaryview;
3757 GtkCheckMenuItem **items;
3761 summaryview = (SummaryView *)data;
3762 g_return_if_fail(summaryview != NULL);
3764 sel = GTK_CLIST(summaryview->ctree)->selection;
3767 menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
3768 g_return_if_fail(menu != NULL);
3770 Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
3772 /* NOTE: don't return prematurely because we set the "dont_toggle"
3773 * state for check menu items */
3774 gtk_object_set_data(GTK_OBJECT(menu), "dont_toggle",
3775 GINT_TO_POINTER(1));
3777 /* clear items. get item pointers. */
3778 for (n = 0, cur = menu->children; cur != NULL; cur = cur->next) {
3779 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
3780 gtk_check_menu_item_set_state
3781 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
3782 items[n] = GTK_CHECK_MENU_ITEM(cur->data);
3787 if (n == (N_COLOR_LABELS + 1)) {
3788 /* iterate all messages and set the state of the appropriate
3790 for (; sel != NULL; sel = sel->next) {
3794 msginfo = gtk_ctree_node_get_row_data
3795 (GTK_CTREE(summaryview->ctree),
3796 GTK_CTREE_NODE(sel->data));
3798 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
3799 if (!items[clabel]->active)
3800 gtk_check_menu_item_set_state
3801 (items[clabel], TRUE);
3805 g_warning("invalid number of color elements (%d)\n", n);
3807 /* reset "dont_toggle" state */
3808 gtk_object_set_data(GTK_OBJECT(menu), "dont_toggle",
3809 GINT_TO_POINTER(0));
3812 static void summary_colorlabel_menu_create(SummaryView *summaryview)
3814 GtkWidget *label_menuitem;
3819 label_menuitem = gtk_item_factory_get_item(summaryview->popupfactory,
3821 gtk_signal_connect(GTK_OBJECT(label_menuitem), "activate",
3822 GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_item_cb),
3824 gtk_widget_show(label_menuitem);
3826 menu = gtk_menu_new();
3828 /* create sub items. for the menu item activation callback we pass the
3829 * index of label_colors[] as data parameter. for the None color we
3830 * pass an invalid (high) value. also we attach a data pointer so we
3831 * can always get back the SummaryView pointer. */
3833 item = gtk_check_menu_item_new_with_label(_("None"));
3834 gtk_menu_append(GTK_MENU(menu), item);
3835 gtk_signal_connect(GTK_OBJECT(item), "activate",
3836 GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_cb),
3837 GUINT_TO_POINTER(0));
3838 gtk_object_set_data(GTK_OBJECT(item), "summaryview", summaryview);
3839 gtk_widget_show(item);
3841 item = gtk_menu_item_new();
3842 gtk_menu_append(GTK_MENU(menu), item);
3843 gtk_widget_show(item);
3845 /* create pixmap/label menu items */
3846 for (i = 0; i < N_COLOR_LABELS; i++) {
3847 item = colorlabel_create_check_color_menu_item(i);
3848 gtk_menu_append(GTK_MENU(menu), item);
3849 gtk_signal_connect(GTK_OBJECT(item), "activate",
3850 GTK_SIGNAL_FUNC(summary_colorlabel_menu_item_activate_cb),
3851 GUINT_TO_POINTER(i + 1));
3852 gtk_object_set_data(GTK_OBJECT(item), "summaryview",
3854 gtk_widget_show(item);
3857 gtk_widget_show(menu);
3858 gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
3859 summaryview->colorlabel_menu = menu;
3862 /* callback functions */
3864 static void summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
3865 SummaryView *summaryview)
3870 if (!summaryview->msg_is_toggled_on && summaryview->selected)
3871 summary_display_msg(summaryview, summaryview->selected, FALSE);
3873 summary_toggle_view(summaryview);
3876 static void summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
3877 SummaryView *summaryview)
3881 if (event->button == 3) {
3882 /* Right button clicked */
3883 /* summary_set_add_sender_menu(summaryview); */
3884 gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
3885 NULL, NULL, event->button, event->time);
3886 } else if (event->button == 2) {
3887 summaryview->display_msg = TRUE;
3888 } else if (event->button == 1) {
3889 if (!prefs_common.emulate_emacs &&
3890 summaryview->msg_is_toggled_on)
3891 summaryview->display_msg = TRUE;
3895 static void summary_button_released(GtkWidget *ctree, GdkEventButton *event,
3896 SummaryView *summaryview)
3900 void summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
3902 summary_key_pressed(summaryview->ctree, event, summaryview);
3905 #define BREAK_ON_MODIFIER_KEY() \
3906 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
3908 #define RETURN_IF_LOCKED() \
3909 if (summaryview->mainwin->lock_count) return
3911 #define KEY_PRESS_EVENT_STOP() \
3912 if (gtk_signal_n_emissions_by_name \
3913 (GTK_OBJECT(ctree), "key_press_event") > 0) { \
3914 gtk_signal_emit_stop_by_name(GTK_OBJECT(ctree), \
3915 "key_press_event"); \
3918 static void summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
3919 SummaryView *summaryview)
3921 GtkCTree *ctree = GTK_CTREE(widget);
3923 FolderItem *to_folder;
3925 if (summary_is_locked(summaryview)) return;
3928 switch (event->keyval) {
3929 case GDK_g: /* Go */
3932 BREAK_ON_MODIFIER_KEY();
3933 KEY_PRESS_EVENT_STOP();
3934 to_folder = foldersel_folder_sel(NULL, NULL);
3936 debug_print(_("Go to %s\n"), to_folder->path);
3937 folderview_select(summaryview->folderview, to_folder);
3940 case GDK_w: /* Write new message */
3941 BREAK_ON_MODIFIER_KEY();
3942 if (summaryview->folder_item) {
3944 ac = summaryview->folder_item->folder->account;
3945 if (ac && ac->protocol == A_NNTP)
3946 compose_new_with_recipient
3947 (ac, summaryview->folder_item->path);
3949 compose_new_with_folderitem(ac, summaryview->folder_item);
3953 case GDK_D: /* Empty trash */
3955 BREAK_ON_MODIFIER_KEY();
3956 KEY_PRESS_EVENT_STOP();
3957 main_window_empty_trash(summaryview->mainwin, TRUE);
3959 case GDK_Q: /* Quit */
3961 BREAK_ON_MODIFIER_KEY();
3963 if (prefs_common.confirm_on_exit) {
3964 if (alertpanel(_("Exit"), _("Exit this program?"),
3965 _("OK"), _("Cancel"), NULL)
3966 == G_ALERTDEFAULT) {
3967 manage_window_focus_in
3968 (summaryview->mainwin->window,
3970 app_will_exit(NULL, summaryview->mainwin);
3974 case GDK_Left: /* Move focus */
3976 gtk_widget_grab_focus(summaryview->folderview->ctree);
3982 if (!summaryview->selected) {
3983 node = gtk_ctree_node_nth(ctree, 0);
3985 gtk_ctree_select(ctree, node);
3990 switch (event->keyval) {
3991 case GDK_space: /* Page down or go to the next */
3992 if (summaryview->displayed != summaryview->selected) {
3993 summary_display_msg(summaryview,
3994 summaryview->selected, FALSE);
3997 if (!textview_scroll_page(summaryview->messageview->textview,
3999 summary_select_next_unread(summaryview);
4001 case GDK_n: /* Next */
4003 BREAK_ON_MODIFIER_KEY();
4004 summary_step(summaryview, GTK_SCROLL_STEP_FORWARD);
4006 case GDK_BackSpace: /* Page up */
4008 textview_scroll_page(summaryview->messageview->textview, TRUE);
4010 case GDK_p: /* Prev */
4012 BREAK_ON_MODIFIER_KEY();
4013 summary_step(summaryview, GTK_SCROLL_STEP_BACKWARD);
4015 case GDK_v: /* Toggle summary mode / message mode */
4017 BREAK_ON_MODIFIER_KEY();
4019 if (!summaryview->msg_is_toggled_on && summaryview->selected)
4020 summary_display_msg(summaryview,
4021 summaryview->selected, FALSE);
4023 summary_toggle_view(summaryview);
4025 case GDK_Return: /* Scroll up/down one line */
4026 if (summaryview->displayed != summaryview->selected) {
4027 summary_display_msg(summaryview,
4028 summaryview->selected, FALSE);
4031 textview_scroll_one_line(summaryview->messageview->textview,
4032 (event->state & GDK_MOD1_MASK) != 0);
4034 case GDK_asterisk: /* Mark */
4035 summary_mark(summaryview);
4037 case GDK_exclam: /* Mark as unread */
4038 summary_mark_as_unread(summaryview);
4040 case GDK_d: /* Delete */
4042 BREAK_ON_MODIFIER_KEY();
4043 summary_delete(summaryview);
4045 case GDK_u: /* Unmark */
4047 BREAK_ON_MODIFIER_KEY();
4048 summary_unmark(summaryview);
4050 case GDK_o: /* Move */
4052 BREAK_ON_MODIFIER_KEY();
4053 summary_move_to(summaryview);
4055 case GDK_O: /* Copy */
4057 BREAK_ON_MODIFIER_KEY();
4058 summary_copy_to(summaryview);
4060 case GDK_x: /* Execute */
4063 BREAK_ON_MODIFIER_KEY();
4064 KEY_PRESS_EVENT_STOP();
4065 summary_execute(summaryview);
4067 case GDK_a: /* Reply to the message */
4068 BREAK_ON_MODIFIER_KEY();
4069 summary_reply_cb(summaryview,
4070 COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE, NULL);
4072 case GDK_A: /* Reply to the message with quotation */
4073 BREAK_ON_MODIFIER_KEY();
4074 summary_reply_cb(summaryview,
4075 COMPOSE_REPLY_TO_ALL_WITH_QUOTE, NULL);
4077 case GDK_f: /* Forward the message */
4078 BREAK_ON_MODIFIER_KEY();
4079 summary_reply_cb(summaryview, COMPOSE_FORWARD, NULL);
4082 BREAK_ON_MODIFIER_KEY();
4083 summary_reply_cb(summaryview, COMPOSE_FORWARD_AS_ATTACH, NULL);
4085 case GDK_y: /* Save the message */
4086 BREAK_ON_MODIFIER_KEY();
4087 summary_save_as(summaryview);
4094 #undef BREAK_ON_MODIFIER_KEY
4095 #undef RETURN_IF_LOCKED
4096 #undef KEY_PRESS_EVENT_STOP
4098 static void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
4100 if (summaryview->folder_item->stype == F_DRAFT ||
4101 summaryview->folder_item->stype == F_OUTBOX ||
4102 summaryview->folder_item->stype == F_QUEUE)
4103 summary_reedit(summaryview);
4105 summary_open_msg(summaryview);
4107 summaryview->display_msg = FALSE;
4110 static void summary_tree_expanded(GtkCTree *ctree, GtkCTreeNode *node,
4111 SummaryView *summaryview)
4113 summary_set_row_marks(summaryview, node);
4116 static void summary_tree_collapsed(GtkCTree *ctree, GtkCTreeNode *node,
4117 SummaryView *summaryview)
4119 summary_set_row_marks(summaryview, node);
4122 static void summary_selected(GtkCTree *ctree, GtkCTreeNode *row,
4123 gint column, SummaryView *summaryview)
4127 summary_status_show(summaryview);
4128 summary_set_menu_sensitive(summaryview);
4130 if (GTK_CLIST(ctree)->selection &&
4131 GTK_CLIST(ctree)->selection->next) {
4132 summaryview->display_msg = FALSE;
4136 summaryview->selected = row;
4138 msginfo = gtk_ctree_node_get_row_data(ctree, row);
4140 switch (column < 0 ? column : summaryview->col_state[column].type) {
4142 if (MSG_IS_MARKED(msginfo->flags)) {
4143 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_MARKED);
4144 CHANGE_FLAGS(msginfo);
4145 summary_set_row_marks(summaryview, row);
4147 summary_mark_row(summaryview, row);
4150 if (MSG_IS_UNREAD(msginfo->flags)) {
4151 summary_mark_row_as_read(summaryview, row);
4152 summary_status_show(summaryview);
4153 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
4154 !MSG_IS_FORWARDED(msginfo->flags)) {
4155 summary_mark_row_as_unread(summaryview, row);
4156 summary_status_show(summaryview);
4163 if (summaryview->display_msg)
4164 summary_display_msg(summaryview, row, FALSE);
4166 summaryview->display_msg = FALSE;
4169 static void summary_col_resized(GtkCList *clist, gint column, gint width,
4170 SummaryView *summaryview)
4172 switch (summaryview->col_state[column].type) {
4174 prefs_common.summary_col_mark = width;
4177 prefs_common.summary_col_unread = width;
4180 prefs_common.summary_col_mime = width;
4183 prefs_common.summary_col_number = width;
4186 prefs_common.summary_col_score = width;
4189 prefs_common.summary_col_size = width;
4192 prefs_common.summary_col_date = width;
4195 prefs_common.summary_col_from = width;
4198 prefs_common.summary_col_subject = width;
4205 static void summary_reply_cb(SummaryView *summaryview, guint action,
4209 GList *sel = GTK_CLIST(summaryview->ctree)->selection;
4211 msginfo = gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
4212 summaryview->selected);
4213 if (!msginfo) return;
4215 switch ((ComposeMode)action) {
4217 compose_reply(msginfo, prefs_common.reply_with_quote,
4220 case COMPOSE_REPLY_WITH_QUOTE:
4221 compose_reply(msginfo, TRUE, FALSE, FALSE);
4223 case COMPOSE_REPLY_WITHOUT_QUOTE:
4224 compose_reply(msginfo, FALSE, FALSE, FALSE);
4226 case COMPOSE_REPLY_TO_SENDER:
4227 compose_reply(msginfo, prefs_common.reply_with_quote,
4230 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
4231 compose_followup_and_reply_to(msginfo,
4232 prefs_common.reply_with_quote,
4235 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
4236 compose_reply(msginfo, TRUE, FALSE, TRUE);
4238 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
4239 compose_reply(msginfo, FALSE, FALSE, TRUE);
4241 case COMPOSE_REPLY_TO_ALL:
4242 compose_reply(msginfo, prefs_common.reply_with_quote,
4245 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
4246 compose_reply(msginfo, TRUE, TRUE, FALSE);
4248 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
4249 compose_reply(msginfo, FALSE, TRUE, FALSE);
4251 case COMPOSE_FORWARD:
4253 compose_forward(NULL, msginfo, FALSE);
4256 /* if (sel->next) FALL THROUGH */
4257 case COMPOSE_FORWARD_AS_ATTACH:
4259 GSList *msginfo_list = NULL;
4260 for ( ; sel != NULL; sel = sel->next)
4261 msginfo_list = g_slist_append(msginfo_list,
4262 gtk_ctree_node_get_row_data(GTK_CTREE(summaryview->ctree),
4263 GTK_CTREE_NODE(sel->data)));
4264 compose_forward_multiple(NULL, msginfo_list);
4265 g_slist_free(msginfo_list);
4269 g_warning("summary_reply_cb(): invalid action: %d\n", action);
4272 summary_set_marks_selected(summaryview);
4275 static void summary_execute_cb(SummaryView *summaryview, guint action,
4278 summary_execute(summaryview);
4281 static void summary_show_all_header_cb(SummaryView *summaryview,
4282 guint action, GtkWidget *widget)
4284 header_window_show_cb(summaryview->mainwin, action, widget);
4287 static void summary_add_address_cb(SummaryView *summaryview,
4288 guint action, GtkWidget *widget)
4290 summary_add_address(summaryview);
4293 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
4295 summary_sort(summaryview, SORT_BY_MARK);
4298 static void summary_unread_clicked(GtkWidget *button, SummaryView *summaryview)
4300 summary_sort(summaryview, SORT_BY_UNREAD);
4303 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
4305 summary_sort(summaryview, SORT_BY_MIME);
4308 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
4310 summary_sort(summaryview, SORT_BY_NUMBER);
4313 static void summary_score_clicked(GtkWidget *button,
4314 SummaryView *summaryview)
4316 summary_sort(summaryview, SORT_BY_SCORE);
4319 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
4321 summary_sort(summaryview, SORT_BY_SIZE);
4324 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
4326 summary_sort(summaryview, SORT_BY_DATE);
4329 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
4331 summary_sort(summaryview, SORT_BY_FROM);
4334 static void summary_subject_clicked(GtkWidget *button,
4335 SummaryView *summaryview)
4337 summary_sort(summaryview, SORT_BY_SUBJECT);
4340 void summary_change_display_item(SummaryView *summaryview)
4342 GtkCList *clist = GTK_CLIST(summaryview->ctree);
4344 #define SET_VISIBLE(col, visible) \
4345 gtk_clist_set_column_visibility(clist, summaryview->col_pos[col], \
4346 prefs_common.visible)
4348 SET_VISIBLE(S_COL_MARK , show_mark);
4349 SET_VISIBLE(S_COL_UNREAD , show_unread);
4350 SET_VISIBLE(S_COL_MIME , show_mime);
4351 SET_VISIBLE(S_COL_NUMBER , show_number);
4352 SET_VISIBLE(S_COL_SCORE , show_score);
4353 SET_VISIBLE(S_COL_SIZE , show_size);
4354 SET_VISIBLE(S_COL_DATE , show_date);
4355 SET_VISIBLE(S_COL_FROM , show_from);
4356 SET_VISIBLE(S_COL_SUBJECT, show_subject);
4361 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
4362 SummaryView *summaryview)
4364 GtkTargetList *list;
4365 GdkDragContext *context;
4367 g_return_if_fail(summaryview != NULL);
4368 g_return_if_fail(summaryview->folder_item != NULL);
4369 g_return_if_fail(summaryview->folder_item->folder != NULL);
4370 if (summaryview->folder_item->folder->type == F_NEWS ||
4371 summaryview->selected == NULL)
4374 list = gtk_target_list_new(summary_drag_types, 1);
4376 context = gtk_drag_begin(widget, list,
4377 GDK_ACTION_MOVE, button, event);
4378 gtk_drag_set_icon_default(context);
4381 static void summary_drag_data_get(GtkWidget *widget,
4382 GdkDragContext *drag_context,
4383 GtkSelectionData *selection_data,
4386 SummaryView *summaryview)
4388 if (info == TARGET_MAIL_URI_LIST) {
4389 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4392 gchar *mail_list = NULL, *tmp1, *tmp2;
4394 for (cur = GTK_CLIST(ctree)->selection;
4395 cur != NULL; cur = cur->next) {
4396 msginfo = gtk_ctree_node_get_row_data
4397 (ctree, GTK_CTREE_NODE(cur->data));
4398 tmp2 = procmsg_get_message_file_path(msginfo);
4399 if (!tmp2) continue;
4400 tmp1 = g_strconcat("file:/", tmp2, NULL);
4406 tmp2 = g_strconcat(mail_list, tmp1, NULL);
4413 if (mail_list != NULL) {
4414 gtk_selection_data_set(selection_data,
4415 selection_data->target, 8,
4416 mail_list, strlen(mail_list));
4419 } else if (info == TARGET_DUMMY) {
4420 if (GTK_CLIST(summaryview->ctree)->selection)
4421 gtk_selection_data_set(selection_data,
4422 selection_data->target, 8,
4428 /* custom compare functions for sorting */
4430 static gint summary_cmp_by_mark(GtkCList *clist,
4431 gconstpointer ptr1, gconstpointer ptr2)
4433 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4434 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4436 return MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags);
4439 static gint summary_cmp_by_unread(GtkCList *clist,
4440 gconstpointer ptr1, gconstpointer ptr2)
4442 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4443 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4445 return MSG_IS_UNREAD(msginfo1->flags) - MSG_IS_UNREAD(msginfo2->flags);
4448 static gint summary_cmp_by_mime(GtkCList *clist,
4449 gconstpointer ptr1, gconstpointer ptr2)
4451 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4452 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4454 return MSG_IS_MIME(msginfo1->flags) - MSG_IS_MIME(msginfo2->flags);
4457 static gint summary_cmp_by_num(GtkCList *clist,
4458 gconstpointer ptr1, gconstpointer ptr2)
4460 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4461 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4463 return msginfo1->msgnum - msginfo2->msgnum;
4466 static gint summary_cmp_by_size(GtkCList *clist,
4467 gconstpointer ptr1, gconstpointer ptr2)
4469 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4470 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4472 return msginfo1->size - msginfo2->size;
4475 static gint summary_cmp_by_date(GtkCList *clist,
4476 gconstpointer ptr1, gconstpointer ptr2)
4478 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4479 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4481 return msginfo1->date_t - msginfo2->date_t;
4484 static gint summary_cmp_by_from(GtkCList *clist,
4485 gconstpointer ptr1, gconstpointer ptr2)
4487 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4488 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4490 if (!msginfo1->fromname)
4491 return (msginfo2->fromname != NULL);
4492 if (!msginfo2->fromname)
4495 return strcasecmp(msginfo1->fromname, msginfo2->fromname);
4498 static gint summary_cmp_by_subject(GtkCList *clist,
4499 gconstpointer ptr1, gconstpointer ptr2)
4501 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4502 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4504 if (!msginfo1->subject)
4505 return (msginfo2->subject != NULL);
4506 if (!msginfo2->subject)
4509 return strcasecmp(msginfo1->subject, msginfo2->subject);
4512 static gint summary_cmp_by_label(GtkCList *clist,
4513 gconstpointer ptr1, gconstpointer ptr2)
4515 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4516 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4518 return MSG_GET_COLORLABEL(msginfo1->flags) -
4519 MSG_GET_COLORLABEL(msginfo2->flags);
4521 static gint summary_cmp_by_score(GtkCList *clist,
4522 gconstpointer ptr1, gconstpointer ptr2)
4524 MsgInfo *msginfo1 = ((GtkCListRow *)ptr1)->data;
4525 MsgInfo *msginfo2 = ((GtkCListRow *)ptr2)->data;
4528 /* if score are equal, sort by date */
4530 diff = msginfo1->threadscore - msginfo2->threadscore;
4534 return summary_cmp_by_date(clist, ptr1, ptr2);
4537 static void summary_ignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
4539 SummaryView *summaryview = (SummaryView *) data;
4542 msginfo = gtk_ctree_node_get_row_data(ctree, row);
4543 if (MSG_IS_NEW(msginfo->flags))
4544 summaryview->newmsgs--;
4545 if (MSG_IS_UNREAD(msginfo->flags))
4546 summaryview->unread--;
4547 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
4549 CHANGE_FLAGS(msginfo);
4551 summary_set_row_marks(summaryview, row);
4552 debug_print(_("Message %d is marked as ignore thread\n"),
4556 static void summary_ignore_thread(SummaryView *summaryview)
4558 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4561 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
4562 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), GTK_CTREE_FUNC(summary_ignore_thread_func), summaryview);
4565 summary_status_show(summaryview);
4568 static void summary_unignore_thread_func(GtkCTree *ctree, GtkCTreeNode *row, gpointer data)
4570 SummaryView *summaryview = (SummaryView *) data;
4573 msginfo = gtk_ctree_node_get_row_data(ctree, row);
4574 if (MSG_IS_NEW(msginfo->flags))
4575 summaryview->newmsgs++;
4576 if (MSG_IS_UNREAD(msginfo->flags))
4577 summaryview->unread++;
4578 MSG_UNSET_PERM_FLAGS(msginfo->flags, MSG_IGNORE_THREAD);
4580 CHANGE_FLAGS(msginfo);
4582 summary_set_row_marks(summaryview, row);
4583 debug_print(_("Message %d is marked as unignore thread\n"),
4587 static void summary_unignore_thread(SummaryView *summaryview)
4589 GtkCTree *ctree = GTK_CTREE(summaryview->ctree);
4592 for (cur = GTK_CLIST(ctree)->selection; cur != NULL; cur = cur->next) {
4593 gtk_ctree_pre_recursive(ctree, GTK_CTREE_NODE(cur->data), GTK_CTREE_FUNC(summary_unignore_thread_func), summaryview);
4596 summary_status_show(summaryview);