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