1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "defs.h"
20 
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 
31 #include "main.h"
32 #include "menu.h"
33 #include "mbox.h"
34 #include "mainwindow.h"
35 #include "folderview.h"
36 #include "summaryview.h"
37 #include "messageview.h"
38 #include "mimeview.h"
39 #include "foldersel.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "sourcewindow.h"
43 #include "prefs_common.h"
44 #include "prefs_summary_column.h"
45 #include "prefs_summary_open.h"
46 #include "prefs_filtering.h"
47 #include "account.h"
48 #include "compose.h"
49 #include "file-utils.h"
50 #include "utils.h"
51 #include "gtkutils.h"
52 #include "stock_pixmap.h"
53 #include "filesel.h"
54 #include "alertpanel.h"
55 #include "inputdialog.h"
56 #include "statusbar.h"
57 #include "folder.h"
58 #include "colorlabel.h"
59 #include "inc.h"
60 #include "imap.h"
61 #ifndef USE_ALT_ADDRBOOK
62 	#include "addressbook.h"
63 #else
64 	#include "addressbook-dbus.h"
65 	#include "addressadd.h"
66 #endif
67 #include "addr_compl.h"
68 #include "folder_item_prefs.h"
69 #include "filtering.h"
70 #include "string_match.h"
71 #include "toolbar.h"
72 #include "news.h"
73 #include "hooks.h"
74 #include "description_window.h"
75 #include "folderutils.h"
76 #include "quicksearch.h"
77 #include "partial_download.h"
78 #include "tags.h"
79 #include "timing.h"
80 #include "log.h"
81 #include "edittags.h"
82 #include "manual.h"
83 #include "manage_window.h"
84 #include "avatars.h"
85 
86 #define SUMMARY_COL_MARK_WIDTH		10
87 #define SUMMARY_COL_STATUS_WIDTH	13
88 #define SUMMARY_COL_LOCKED_WIDTH	13
89 #define SUMMARY_COL_MIME_WIDTH		11
90 
91 static int normal_row_height = -1;
92 static GtkStyle *bold_style;
93 static GtkStyle *bold_marked_style;
94 static GtkStyle *bold_deleted_style;
95 static GtkStyle *small_style;
96 static GtkStyle *small_marked_style;
97 static GtkStyle *small_deleted_style;
98 
99 static GdkPixbuf *markxpm;
100 static GdkPixbuf *deletedxpm;
101 static GdkPixbuf *movedxpm;
102 static GdkPixbuf *copiedxpm;
103 
104 static GdkPixbuf *newxpm;
105 static GdkPixbuf *unreadxpm;
106 static GdkPixbuf *repliedxpm;
107 static GdkPixbuf *forwardedxpm;
108 static GdkPixbuf *repliedandforwardedxpm;
109 static GdkPixbuf *ignorethreadxpm;
110 static GdkPixbuf *watchthreadxpm;
111 static GdkPixbuf *lockedxpm;
112 static GdkPixbuf *spamxpm;
113 
114 static GdkPixbuf *clipxpm;
115 static GdkPixbuf *keyxpm;
116 static GdkPixbuf *clipkeyxpm;
117 static GdkPixbuf *keysignxpm;
118 static GdkPixbuf *gpgsignedxpm;
119 static GdkPixbuf *clipgpgsignedxpm;
120 
121 static void summary_free_msginfo_func	(GtkCMCTree		*ctree,
122 					 GtkCMCTreeNode		*node,
123 					 gpointer		 data);
124 static void summary_set_marks_func	(GtkCMCTree		*ctree,
125 					 GtkCMCTreeNode		*node,
126 					 gpointer		 data);
127 
128 void  summary_set_menu_sensitive	(SummaryView		*summaryview);
129 guint summary_get_msgnum		(SummaryView		*summaryview,
130 					 GtkCMCTreeNode		*node);
131 
132 
133 static void summary_set_hide_menu (SummaryView *summaryview,
134 				   const gchar *menu_item,
135 				   guint action);
136 
137 static GtkCMCTreeNode *summary_find_prev_msg
138 					(SummaryView		*summaryview,
139 					 GtkCMCTreeNode		*current_node,
140 					 gboolean	 	 start_from_prev);
141 static GtkCMCTreeNode *summary_find_next_msg
142 					(SummaryView		*summaryview,
143 					 GtkCMCTreeNode		*current_node,
144 					 gboolean	 	 start_from_next);
145 
146 static GtkCMCTreeNode *summary_find_prev_flagged_msg
147 					(SummaryView	*summaryview,
148 					 GtkCMCTreeNode	*current_node,
149 					 MsgPermFlags	 flags,
150 					 gboolean	 start_from_prev);
151 static GtkCMCTreeNode *summary_find_next_flagged_msg
152 					(SummaryView	*summaryview,
153 					 GtkCMCTreeNode	*current_node,
154 					 MsgPermFlags	 flags,
155 					 gboolean	 start_from_next);
156 
157 static GtkCMCTreeNode *summary_find_msg_by_msgnum
158 					(SummaryView		*summaryview,
159 					 guint			 msgnum);
160 
161 static void summary_update_status	(SummaryView		*summaryview);
162 
163 /* display functions */
164 static void summary_status_show		(SummaryView		*summaryview);
165 static void summary_set_column_titles	(SummaryView		*summaryview);
166 static void summary_set_ctree_from_list	(SummaryView		*summaryview,
167 					 GSList			*mlist,
168 					 guint			selected_msgnum);
169 static inline void summary_set_header	(SummaryView		*summaryview,
170 					 gchar			*text[],
171 					 MsgInfo		*msginfo);
172 static void summary_display_msg		(SummaryView		*summaryview,
173 					 GtkCMCTreeNode		*row);
174 static void summary_display_msg_full	(SummaryView		*summaryview,
175 					 GtkCMCTreeNode		*row,
176 					 gboolean		 new_window,
177 					 gboolean		 all_headers);
178 static void summary_set_row_marks	(SummaryView		*summaryview,
179 					 GtkCMCTreeNode		*row);
180 
181 static gboolean summary_set_row_tag	(SummaryView 		*summaryview,
182 					 GtkCMCTreeNode 		*row,
183 					 gboolean		 refresh,
184 					 gboolean 		 set,
185 					 gint 			 id);
186 /* message handling */
187 static void summary_mark_row		(SummaryView		*summaryview,
188 					 GtkCMCTreeNode		*row);
189 static void summary_lock_row		(SummaryView		*summaryview,
190 					 GtkCMCTreeNode		*row);
191 static void summary_unlock_row		(SummaryView		*summaryview,
192 					 GtkCMCTreeNode		*row);
193 static void summary_mark_row_as_read	(SummaryView		*summaryview,
194 					 GtkCMCTreeNode		*row);
195 static void summary_mark_row_as_unread	(SummaryView		*summaryview,
196 					 GtkCMCTreeNode		*row);
197 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed);
198 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed);
199 static void summary_delete_row		(SummaryView		*summaryview,
200 					 GtkCMCTreeNode		*row);
201 static void summary_unmark_row		(SummaryView		*summaryview,
202 					 GtkCMCTreeNode		*row);
203 static void summary_move_row_to		(SummaryView		*summaryview,
204 					 GtkCMCTreeNode		*row,
205 					 FolderItem		*to_folder);
206 static void summary_copy_row_to		(SummaryView		*summaryview,
207 					 GtkCMCTreeNode		*row,
208 					 FolderItem		*to_folder);
209 
210 static gint summary_execute_move	(SummaryView		*summaryview);
211 static void summary_execute_move_func	(GtkCMCTree		*ctree,
212 					 GtkCMCTreeNode		*node,
213 					 gpointer		 data);
214 static void summary_execute_copy	(SummaryView		*summaryview);
215 static void summary_execute_copy_func	(GtkCMCTree		*ctree,
216 					 GtkCMCTreeNode		*node,
217 					 gpointer		 data);
218 static void summary_execute_delete	(SummaryView		*summaryview);
219 static void summary_execute_delete_func	(GtkCMCTree		*ctree,
220 					 GtkCMCTreeNode		*node,
221 					 gpointer		 data);
222 static void summary_execute_expunge	(SummaryView		*summaryview);
223 
224 static void summary_thread_init		(SummaryView		*summaryview);
225 
226 static void summary_unthread_for_exec		(SummaryView	*summaryview);
227 static void summary_unthread_for_exec_func	(GtkCMCTree	*ctree,
228 						 GtkCMCTreeNode	*node,
229 						 gpointer	 data);
230 
231 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
232 			      GSList * mlist);
233 
234 static void summary_filter_func		(MsgInfo		*msginfo,
235 					 PrefsAccount		*ac_prefs);
236 
237 static void summary_colorlabel_menu_item_activate_cb
238 					  (GtkWidget	*widget,
239 					   gpointer	 data);
240 static void summary_colorlabel_menu_item_activate_item_cb
241 					  (GtkMenuItem	*label_menu_item,
242 					   gpointer	 data);
243 static void summary_colorlabel_menu_create(SummaryView	*summaryview,
244 					   gboolean  refresh);
245 static void summary_tags_menu_item_activate_cb
246 					  (GtkWidget	*widget,
247 					   gpointer	 data);
248 static void summary_tags_menu_item_activate_item_cb
249 					  (GtkMenuItem	*label_menu_item,
250 					   gpointer	 data);
251 static void summary_tags_menu_create(SummaryView	*summaryview,
252 					   gboolean  refresh);
253 
254 static GtkWidget *summary_ctree_create	(SummaryView	*summaryview);
255 
256 /* callback functions */
257 static gint summary_toggle_pressed	(GtkWidget		*eventbox,
258 					 GdkEventButton		*event,
259 					 SummaryView		*summaryview);
260 #ifdef GENERIC_UMPC
261 static void summary_toggle_multiple_pressed
262 					(GtkWidget		*widget,
263 					 SummaryView		*summaryview);
264 #endif
265 static gint summary_folder_eventbox_pressed
266 					(GtkWidget		*eventbox,
267 					 GdkEventButton		*event,
268 					 SummaryView		*summaryview);
269 static gboolean summary_button_pressed	(GtkWidget		*ctree,
270 					 GdkEventButton		*event,
271 					 SummaryView		*summaryview);
272 static gboolean summary_button_released	(GtkWidget		*ctree,
273 					 GdkEventButton		*event,
274 					 SummaryView		*summaryview);
275 static gboolean summary_key_pressed	(GtkWidget		*ctree,
276 					 GdkEventKey		*event,
277 					 SummaryView		*summaryview);
278 static void summary_tree_expanded	(GtkCMCTree		*ctree,
279 					 GtkCMCTreeNode		*node,
280 					 SummaryView		*summaryview);
281 static void summary_tree_collapsed	(GtkCMCTree		*ctree,
282 					 GtkCMCTreeNode		*node,
283 					 SummaryView		*summaryview);
284 static void summary_selected		(GtkCMCTree		*ctree,
285 					 GtkCMCTreeNode		*row,
286 					 gint			 column,
287 					 SummaryView		*summaryview);
288 static void summary_unselected		(GtkCMCTree		*ctree,
289 					 GtkCMCTreeNode		*row,
290 					 gint			 column,
291 					 SummaryView		*summaryview);
292 static void summary_col_resized		(GtkCMCList		*clist,
293 					 gint			 column,
294 					 gint			 width,
295 					 SummaryView		*summaryview);
296 static void summary_mark_clicked	(GtkWidget		*button,
297 					 SummaryView		*summaryview);
298 static void summary_status_clicked	(GtkWidget		*button,
299 					 SummaryView		*summaryview);
300 static void summary_mime_clicked	(GtkWidget		*button,
301 					 SummaryView		*summaryview);
302 static void summary_num_clicked		(GtkWidget		*button,
303 					 SummaryView		*summaryview);
304 static void summary_score_clicked       (GtkWidget *button,
305 					 SummaryView *summaryview);
306 static void summary_size_clicked	(GtkWidget		*button,
307 					 SummaryView		*summaryview);
308 static void summary_date_clicked	(GtkWidget		*button,
309 					 SummaryView		*summaryview);
310 static void summary_from_clicked	(GtkWidget		*button,
311 					 SummaryView		*summaryview);
312 static void summary_to_clicked		(GtkWidget		*button,
313 					 SummaryView		*summaryview);
314 static void summary_subject_clicked	(GtkWidget		*button,
315 					 SummaryView		*summaryview);
316 static void summary_score_clicked	(GtkWidget		*button,
317 					 SummaryView		*summaryview);
318 static void summary_locked_clicked	(GtkWidget		*button,
319 					 SummaryView		*summaryview);
320 static void summary_tags_clicked	(GtkWidget		*button,
321 					 SummaryView		*summaryview);
322 
323 static void summary_start_drag		(GtkWidget        *widget,
324 					 int button,
325 					 GdkEvent *event,
326 					 SummaryView      *summaryview);
327 static void summary_drag_data_get       (GtkWidget        *widget,
328 					 GdkDragContext   *drag_context,
329 					 GtkSelectionData *selection_data,
330 					 guint             info,
331 					 guint             time,
332 					 SummaryView      *summaryview);
333 static void summary_drag_data_received(GtkWidget        *widget,
334 					GdkDragContext   *drag_context,
335 					gint              x,
336 					gint              y,
337 					GtkSelectionData *data,
338 					guint             info,
339 					guint             time,
340 					SummaryView       *summaryview);
341 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
342 					  GdkDragContext *context,
343 					  gint            x,
344 					  gint            y,
345 					  guint           time,
346 					  SummaryView	 *summaryview);
347 static void summary_drag_end(GtkWidget *widget,
348 					  GdkDragContext *drag_context,
349 					  SummaryView 	 *summaryview);
350 
351 /* custom compare functions for sorting */
352 static gint summary_cmp_by_mark		(GtkCMCList		*clist,
353 					 gconstpointer		 ptr1,
354 					 gconstpointer		 ptr2);
355 static gint summary_cmp_by_status	(GtkCMCList		*clist,
356 					 gconstpointer		 ptr1,
357 					 gconstpointer		 ptr2);
358 static gint summary_cmp_by_mime		(GtkCMCList		*clist,
359 					 gconstpointer		 ptr1,
360 					 gconstpointer		 ptr2);
361 static gint summary_cmp_by_num		(GtkCMCList		*clist,
362 					 gconstpointer		 ptr1,
363 					 gconstpointer		 ptr2);
364 static gint summary_cmp_by_size		(GtkCMCList		*clist,
365 					 gconstpointer		 ptr1,
366 					 gconstpointer		 ptr2);
367 static gint summary_cmp_by_date		(GtkCMCList		*clist,
368 					 gconstpointer		 ptr1,
369 					 gconstpointer		 ptr2);
370 static gint summary_cmp_by_thread_date	(GtkCMCList		*clist,
371 					 gconstpointer		 ptr1,
372 					 gconstpointer		 ptr2);
373 static gint summary_cmp_by_from		(GtkCMCList		*clist,
374 					 gconstpointer		 ptr1,
375 					 gconstpointer		 ptr2);
376 static gint summary_cmp_by_simplified_subject
377 					(GtkCMCList 		*clist,
378 					 gconstpointer 		 ptr1,
379 					 gconstpointer 		 ptr2);
380 static gint summary_cmp_by_score	(GtkCMCList		*clist,
381 					 gconstpointer		 ptr1,
382 					 gconstpointer		 ptr2);
383 static gint summary_cmp_by_label	(GtkCMCList		*clist,
384 					 gconstpointer		 ptr1,
385 					 gconstpointer		 ptr2);
386 static gint summary_cmp_by_to		(GtkCMCList		*clist,
387 					 gconstpointer		 ptr1,
388 					 gconstpointer		 ptr2);
389 static gint summary_cmp_by_subject	(GtkCMCList		*clist,
390 					 gconstpointer		 ptr1,
391 					 gconstpointer		 ptr2);
392 static gint summary_cmp_by_locked	(GtkCMCList 		*clist,
393 				         gconstpointer 		 ptr1,
394 					 gconstpointer 		 ptr2);
395 static gint summary_cmp_by_tags		(GtkCMCList 		*clist,
396 				         gconstpointer 		 ptr1,
397 					 gconstpointer 		 ptr2);
398 
399 static void quicksearch_execute_cb	(QuickSearch    *quicksearch,
400 					 gpointer	 data);
401 
402 static void tog_searchbar_cb		(GtkWidget	*w,
403 					 gpointer	 data);
404 
405 static void summary_find_answers	(SummaryView 	*summaryview,
406 					 MsgInfo	*msg);
407 
408 static gboolean summary_update_msg	(gpointer source, gpointer data);
409 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
410 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
411 static void summary_set_colorlabel_color (GtkCMCTree		*ctree,
412 				   GtkCMCTreeNode		*node,
413 				   guint		 labelcolor);
414 static void summary_thread_build(SummaryView *summaryview);
415 
416 GtkTargetEntry summary_drag_types[3] =
417 {
418 	{"text/uri-list", 0, TARGET_MAIL_URI_LIST},
419 	{"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
420 	{"claws-mail/msg-path-list", 0, TARGET_MAIL_CM_PATH_LIST},
421 };
422 
423 static void summary_reedit_cb(GtkAction *gaction, gpointer data);
424 static void summary_reply_cb(GtkAction *gaction, gpointer data);
425 
426 /* Only submenus and specifically-handled menu entries here */
427 static GtkActionEntry summary_popup_entries[] =
428 {
429 	{"SummaryViewPopup",                      NULL, "SummaryViewPopup", NULL, NULL, NULL },
430 	{"SummaryViewPopup/Reedit",               NULL, N_("Re-edit"), NULL, NULL, G_CALLBACK(summary_reedit_cb) },
431 	{"SummaryViewPopup/Reply",                NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY */
432 	{"SummaryViewPopup/ReplyTo",              NULL, N_("Repl_y to"), NULL, NULL, NULL },
433 	{"SummaryViewPopup/ReplyTo/All",          NULL, N_("_All"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_ALL */
434 	{"SummaryViewPopup/ReplyTo/Sender",       NULL, N_("_Sender"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_SENDER */
435 	{"SummaryViewPopup/ReplyTo/List",         NULL, N_("Mailing _list"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_LIST */
436 
437 	{"SummaryViewPopup/Forward",              NULL, N_("_Forward"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_INLINE */
438 	{"SummaryViewPopup/ForwardAtt",           NULL, N_("For_ward as attachment"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_AS_ATTACH */
439 	{"SummaryViewPopup/Redirect",             NULL, N_("Redirec_t"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REDIRECT */
440 	{"SummaryViewPopup/Mark",                 NULL, N_("_Mark"), NULL, NULL, NULL },
441 	{"SummaryViewPopup/ColorLabel",           NULL, N_("Color la_bel"), NULL, NULL, NULL },
442 	{"SummaryViewPopup/Tags",                 NULL, N_("Ta_gs"), NULL, NULL, NULL },
443 	{"SummaryViewPopup/CreateFilterRule",     NULL, N_("Create _filter rule"), NULL, NULL, NULL },
444 #ifndef GENERIC_UMPC
445 	{"SummaryViewPopup/CreateProcessingRule", NULL, N_("Create processing rule"), NULL, NULL, NULL },
446 #endif
447 	{"SummaryViewPopup/View",                 NULL, N_("_View"), NULL, NULL, NULL },
448 };
449 
450 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data);
451 static void summary_header_set_displayed_columns_cb(GtkAction *gaction, gpointer data);
452 
453 static GtkActionEntry summary_header_popup_entries[] =
454 {
455 	{"SummaryViewHeaderPopup",                     NULL, "SummaryViewHeaderPopup", NULL, NULL, NULL },
456 	{"SummaryViewHeaderPopup/SetDisplayedColumns", NULL, N_("_Set displayed columns"), NULL, NULL, G_CALLBACK(summary_header_set_displayed_columns_cb) }
457 };
458 
459 static GtkToggleActionEntry summary_header_popup_toggle_entries[] =
460 {
461 	{"SummaryViewHeaderPopup/LockColumnHeaders",     NULL, N_("_Lock column headers"), NULL, NULL, G_CALLBACK(summary_header_lock_sorting_cb), FALSE }
462 };
463 
464 static const gchar *const col_label[N_SUMMARY_COLS] = {
465 	"",            /* S_COL_MARK    */
466 	N_("S"),       /* S_COL_STATUS  */
467 	"",            /* S_COL_MIME    */
468 	N_("Subject"), /* S_COL_SUBJECT */
469 	N_("From"),    /* S_COL_FROM    */
470 	N_("To"),      /* S_COL_TO      */
471 	N_("Date"),    /* S_COL_DATE    */
472 	N_("Size"),    /* S_COL_SIZE    */
473 	N_("#"),       /* S_COL_NUMBER  */
474 	N_("Score"),   /* S_COL_SCORE   */
475 	"",            /* S_COL_LOCKED  */
476 	N_("Tags"),    /* S_COL_TAGS    */
477 };
478 
summary_freeze(SummaryView * summaryview)479 void summary_freeze(SummaryView *summaryview)
480 {
481 	if (summaryview)
482 		gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree));
483 }
484 
summary_thaw(SummaryView * summaryview)485 void summary_thaw(SummaryView *summaryview)
486 {
487 	if (summaryview)
488 		gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
489 }
490 
summary_thaw_with_status(SummaryView * summaryview)491 void summary_thaw_with_status(SummaryView *summaryview)
492 {
493 	if (summaryview) {
494 		summary_status_show(summaryview);
495 		gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
496 	}
497 }
498 
summary_grab_focus(SummaryView * summaryview)499 void summary_grab_focus(SummaryView *summaryview)
500 {
501 	if (summaryview)
502 		gtk_widget_grab_focus(summaryview->ctree);
503 }
504 
summary_get_main_widget(SummaryView * summaryview)505 GtkWidget *summary_get_main_widget(SummaryView *summaryview)
506 {
507 	if (summaryview)
508 		return summaryview->ctree;
509 	else
510 		return NULL;
511 }
512 
513 #define START_LONG_OPERATION(summaryview,force_freeze) {	\
514 	summary_lock(summaryview);				\
515 	main_window_cursor_wait(summaryview->mainwin);		\
516 	if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\
517 		froze = TRUE;						\
518 		summary_freeze(summaryview);	\
519 	}							\
520 	folder_item_update_freeze();				\
521 	inc_lock();						\
522 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,		\
523 		      summaryview->msginfo_update_callback_id);	\
524 }
525 #define END_LONG_OPERATION(summaryview) {			\
526 	inc_unlock();						\
527 	folder_item_update_thaw();				\
528 	if (froze) 						\
529 		summary_thaw(summaryview);	\
530 	main_window_cursor_normal(summaryview->mainwin);	\
531 	summary_unlock(summaryview);				\
532 	summaryview->msginfo_update_callback_id =		\
533 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 	\
534 		summary_update_msg, (gpointer) summaryview);	\
535 }
536 
popup_menu_selection_done(GtkMenuShell * shell,gpointer user_data)537 static void popup_menu_selection_done(GtkMenuShell *shell, gpointer user_data)
538 {
539 	SummaryView *summaryview = (SummaryView *)user_data;
540 
541 	cm_return_if_fail(summaryview != NULL);
542 
543 	/* If a message is displayed, place cursor back on the message. */
544 	if (summaryview->displayed != NULL &&
545 			summaryview->displayed != summaryview->selected) {
546 		gtk_sctree_select(GTK_SCTREE(summaryview->ctree), summaryview->displayed);
547 	}
548 }
549 
summary_create(MainWindow * mainwin)550 SummaryView *summary_create(MainWindow *mainwin)
551 {
552 	SummaryView *summaryview;
553 	GtkWidget *vbox;
554 	GtkWidget *scrolledwin;
555 	GtkWidget *ctree;
556 	GtkWidget *hbox;
557 	GtkWidget *hbox_l;
558 	GtkWidget *stat_box;
559 	GtkWidget *stat_box2;
560 	GtkWidget *stat_vbox;
561 	GtkWidget *statlabel_folder;
562 	GtkWidget *statlabel_select;
563 	GtkWidget *statlabel_msgs;
564 	GtkWidget *hbox_spc;
565 	GtkWidget *toggle_eventbox;
566 #ifdef GENERIC_UMPC
567 	GtkWidget *multiple_sel_togbtn;
568 #endif
569 	GtkWidget *toggle_arrow;
570 	GtkWidget *toggle_search;
571 	QuickSearch *quicksearch;
572 
573 	debug_print("Creating summary view...\n");
574 	summaryview = g_new0(SummaryView, 1);
575 
576 #define SUMMARY_VBOX_SPACING 3
577 	vbox = gtk_vbox_new(FALSE, SUMMARY_VBOX_SPACING);
578 
579 	/* create status label */
580 	hbox = gtk_hbox_new(FALSE, 0);
581 	gtk_widget_show(hbox);
582 
583 	stat_vbox = gtk_vbox_new(FALSE, 0);
584 	gtk_widget_show(stat_vbox);
585 
586 	stat_box = gtk_hbox_new(FALSE, 0);
587 	gtk_widget_show(stat_box);
588 
589 	stat_box2 = gtk_hbox_new(FALSE, 0);
590 	gtk_widget_show(stat_box2);
591 
592 	toggle_search = gtk_toggle_button_new();
593 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_search),
594 				     prefs_common.show_searchbar);
595 	gtk_widget_set_can_focus(toggle_search, FALSE);
596 	gtk_widget_show(toggle_search);
597 
598 	CLAWS_SET_TIP(toggle_search, _("Toggle quick search bar"));
599 
600 	gtk_box_pack_start(GTK_BOX(hbox), toggle_search, FALSE, FALSE, 2);
601 
602 	gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0);
603 	gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0);
604 	gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0);
605 
606 	hbox_l = gtk_hbox_new(FALSE, 0);
607 	gtk_widget_show(hbox_l);
608 	gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0);
609 
610 	statlabel_folder = gtk_label_new("");
611 	gtk_widget_show(statlabel_folder);
612 	gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
613 	statlabel_select = gtk_label_new("");
614 	gtk_widget_show(statlabel_select);
615 	gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
616 
617 	/* toggle view button */
618 	toggle_eventbox = gtk_event_box_new();
619 	gtk_widget_show(toggle_eventbox);
620 
621 	gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
622 
623 	toggle_arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
624 	gtk_widget_show(toggle_arrow);
625 	gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
626 	g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
627 			 G_CALLBACK(summary_toggle_pressed),
628 			 summaryview);
629 
630 #ifdef GENERIC_UMPC
631 	multiple_sel_togbtn = gtk_toggle_button_new();
632 	gtk_widget_show(multiple_sel_togbtn);
633 	gtk_box_pack_end(GTK_BOX(hbox), multiple_sel_togbtn, FALSE, FALSE, 4);
634 	CLAWS_SET_TIP(multiple_sel_togbtn,
635 			     _("Toggle multiple selection"));
636 	g_signal_connect(G_OBJECT(multiple_sel_togbtn), "toggled",
637 			 G_CALLBACK(summary_toggle_multiple_pressed),
638 			 summaryview);
639 #endif
640 
641 	statlabel_msgs = gtk_label_new("");
642 	gtk_widget_show(statlabel_msgs);
643 	gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, 4);
644 
645 	hbox_spc = gtk_hbox_new(FALSE, 0);
646 	gtk_widget_show(hbox_spc);
647 	gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
648 
649 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
650 	gtk_widget_show(scrolledwin);
651 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
652 				       GTK_POLICY_AUTOMATIC,
653 				       GTK_POLICY_AUTOMATIC);
654 	summaryview->mainwidget_book = gtk_notebook_new();
655         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
656         gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
657 #ifndef GENERIC_UMPC
658 	gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
659 		scrolledwin);
660 	gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
661 #endif
662 	gtk_widget_set_size_request(vbox,
663 			     prefs_common.summaryview_width,
664 			     prefs_common.summaryview_height);
665 
666 	ctree = summary_ctree_create(summaryview);
667 	gtk_widget_show(ctree);
668 
669 	gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
670 					    GTK_CMCLIST(ctree)->hadjustment);
671 	gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
672 					    GTK_CMCLIST(ctree)->vadjustment);
673 	gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
674 
675 	/* status label */
676 	gtk_widget_show_all(stat_vbox);
677 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
678 
679 	/* quick search */
680 	quicksearch = quicksearch_new();
681 	gtk_box_pack_start(GTK_BOX(vbox), quicksearch_get_widget(quicksearch), FALSE, FALSE, 0);
682 
683 #ifdef GENERIC_UMPC
684 	gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
685 		scrolledwin);
686 	gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
687 #endif
688 	quicksearch_set_execute_callback(quicksearch, quicksearch_execute_cb, summaryview);
689 
690   	g_signal_connect (G_OBJECT(toggle_search), "toggled",
691 			  G_CALLBACK(tog_searchbar_cb), summaryview);
692 
693 	/* create popup menu */
694 
695 	gtk_action_group_add_actions(mainwin->action_group,
696 			summary_popup_entries,
697 			G_N_ELEMENTS(summary_popup_entries),
698 			(gpointer)summaryview);
699 
700 	gtk_action_group_add_actions(mainwin->action_group,
701 			summary_header_popup_entries,
702 			G_N_ELEMENTS(summary_header_popup_entries),
703 			(gpointer)summaryview);
704 
705 	gtk_action_group_add_toggle_actions(mainwin->action_group,
706 			summary_header_popup_toggle_entries,
707 			G_N_ELEMENTS(summary_header_popup_toggle_entries),
708 			(gpointer)summaryview);
709 
710 	summaryview->ui_manager = gtk_ui_manager_new();
711 	summaryview->action_group = cm_menu_create_action_group_full(summaryview->ui_manager,"Menu", summary_popup_entries,
712 			G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview);
713 
714 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU)
715 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reedit", "SummaryViewPopup/Reedit", GTK_UI_MANAGER_MENUITEM)
716 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reply", "SummaryViewPopup/Reply", GTK_UI_MANAGER_MENUITEM)
717 #ifndef GENERIC_UMPC
718 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU)
719 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
720 #endif
721 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Forward", "SummaryViewPopup/Forward", GTK_UI_MANAGER_MENUITEM)
722 #ifndef GENERIC_UMPC
723 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ForwardAtt", "SummaryViewPopup/ForwardAtt", GTK_UI_MANAGER_MENUITEM)
724 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Redirect", "SummaryViewPopup/Redirect", GTK_UI_MANAGER_MENUITEM)
725 #endif
726 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
727 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Move", "Message/Move", GTK_UI_MANAGER_MENUITEM)
728 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Copy", "Message/Copy", GTK_UI_MANAGER_MENUITEM)
729 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Trash", "Message/Trash", GTK_UI_MANAGER_MENUITEM)
730 #ifndef GENERIC_UMPC
731 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Delete", "Message/Delete", GTK_UI_MANAGER_MENUITEM)
732 #endif
733 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
734 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Mark", "SummaryViewPopup/Mark", GTK_UI_MANAGER_MENU)
735 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ColorLabel", "SummaryViewPopup/ColorLabel", GTK_UI_MANAGER_MENU)
736 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Tags", "SummaryViewPopup/Tags", GTK_UI_MANAGER_MENU)
737 
738 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
739 #ifndef GENERIC_UMPC
740 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "AddSenderToAB", "Tools/AddSenderToAB", GTK_UI_MANAGER_MENUITEM)
741 #endif
742 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateFilterRule", "SummaryViewPopup/CreateFilterRule", GTK_UI_MANAGER_MENU)
743 #ifndef GENERIC_UMPC
744 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateProcessingRule", "SummaryViewPopup/CreateProcessingRule", GTK_UI_MANAGER_MENU)
745 #endif
746 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator5", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
747 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU)
748 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "SaveAs", "File/SaveAs", GTK_UI_MANAGER_MENUITEM)
749 #ifndef GENERIC_UMPC
750 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Print", "File/Print", GTK_UI_MANAGER_MENUITEM)
751 #endif
752 	/* last separator, for plugins */
753 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator6", "File/---", GTK_UI_MANAGER_SEPARATOR)
754 
755 	/* submenus - replyto */
756 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "All", "SummaryViewPopup/ReplyTo/All", GTK_UI_MANAGER_MENUITEM)
757 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "Sender", "SummaryViewPopup/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM)
758 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "MailingList", "SummaryViewPopup/ReplyTo/List", GTK_UI_MANAGER_MENUITEM)
759 
760 	/* submenus - mark */
761 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Mark", "Message/Mark/Mark", GTK_UI_MANAGER_MENUITEM)
762 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unmark", "Message/Mark/Unmark", GTK_UI_MANAGER_MENUITEM)
763 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator1", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
764 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkRead", "Message/Mark/MarkRead", GTK_UI_MANAGER_MENUITEM)
765 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkUnread", "Message/Mark/MarkUnread", GTK_UI_MANAGER_MENUITEM)
766 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator2", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
767 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "IgnoreThread", "Message/Mark/IgnoreThread", GTK_UI_MANAGER_MENUITEM)
768 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnignoreThread", "Message/Mark/UnignoreThread", GTK_UI_MANAGER_MENUITEM)
769 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "WatchThread", "Message/Mark/WatchThread", GTK_UI_MANAGER_MENUITEM)
770 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnwatchThread", "Message/Mark/UnwatchThread", GTK_UI_MANAGER_MENUITEM)
771 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator3", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
772 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkSpam", "Message/Mark/MarkSpam", GTK_UI_MANAGER_MENUITEM)
773 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkHam", "Message/Mark/MarkHam", GTK_UI_MANAGER_MENUITEM)
774 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator4", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
775 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Lock", "Message/Mark/Lock", GTK_UI_MANAGER_MENUITEM)
776 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unlock", "Message/Mark/Unlock", GTK_UI_MANAGER_MENUITEM)
777 
778 	/* submenus - colorlabel and tags are dynamic */
779 	/* submenus - createfilterrule */
780 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "Automatically", "Tools/CreateFilterRule/Automatically", GTK_UI_MANAGER_MENUITEM)
781 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByFrom", "Tools/CreateFilterRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
782 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByTo", "Tools/CreateFilterRule/ByTo", GTK_UI_MANAGER_MENUITEM)
783 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "BySubject", "Tools/CreateFilterRule/BySubject", GTK_UI_MANAGER_MENUITEM)
784 
785 #ifndef GENERIC_UMPC
786 	/* submenus - createprocessingrule */
787 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "Automatically", "Tools/CreateProcessingRule/Automatically", GTK_UI_MANAGER_MENUITEM)
788 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByFrom", "Tools/CreateProcessingRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
789 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByTo", "Tools/CreateProcessingRule/ByTo", GTK_UI_MANAGER_MENUITEM)
790 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "BySubject", "Tools/CreateProcessingRule/BySubject", GTK_UI_MANAGER_MENUITEM)
791 #endif
792 
793 	/* submenus - view */
794 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "OpenNewWindow", "View/OpenNewWindow", GTK_UI_MANAGER_MENUITEM)
795 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "MessageSource", "View/MessageSource", GTK_UI_MANAGER_MENUITEM)
796 #ifndef GENERIC_UMPC
797 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "AllHeaders", "View/AllHeaders", GTK_UI_MANAGER_MENUITEM)
798 #endif
799 
800 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewHeaderPopup", "SummaryViewHeaderPopup", GTK_UI_MANAGER_MENU)
801 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "LockColumnHeaders", "SummaryViewHeaderPopup/LockColumnHeaders", GTK_UI_MANAGER_MENUITEM)
802 	MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "SetDisplayedColumns", "SummaryViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
803 
804 	summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
805 				gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewPopup")) );
806 	summaryview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
807 				gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup")) );
808 
809 	summaryview->vbox = vbox;
810 	summaryview->scrolledwin = scrolledwin;
811 	summaryview->ctree = ctree;
812 	summaryview->hbox = hbox;
813 	summaryview->hbox_l = hbox_l;
814 	summaryview->hbox_spc = hbox_spc;
815 	summaryview->stat_box = stat_box;
816 	summaryview->stat_box2 = stat_box2;
817 	summaryview->statlabel_folder = statlabel_folder;
818 	summaryview->statlabel_select = statlabel_select;
819 	summaryview->statlabel_msgs = statlabel_msgs;
820 	summaryview->toggle_eventbox = toggle_eventbox;
821 	summaryview->toggle_arrow = toggle_arrow;
822 #ifdef GENERIC_UMPC
823 	summaryview->multiple_sel_togbtn = multiple_sel_togbtn;
824 #endif
825 	summaryview->toggle_search = toggle_search;
826 	summaryview->lock_count = 0;
827 	summaryview->msginfo_update_callback_id =
828 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
829 	summaryview->folder_item_update_callback_id =
830 		hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
831 				summary_update_folder_item_hook,
832 				(gpointer) summaryview);
833 	summaryview->folder_update_callback_id =
834 		hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
835 				summary_update_folder_hook,
836 				(gpointer) summaryview);
837 
838 	summaryview->target_list = gtk_target_list_new(summary_drag_types, 3);
839 
840 	summaryview->quicksearch = quicksearch;
841 
842 	/* CLAWS: need this to get the SummaryView * from
843 	 * the CList */
844 	g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
845 
846 	gtk_widget_show_all(vbox);
847 
848 	gtk_widget_show(vbox);
849 
850 	if (prefs_common.show_searchbar)
851 		quicksearch_show(quicksearch);
852 	else
853 		quicksearch_hide(quicksearch);
854 
855 	if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
856 	    prefs_common.layout_mode == SMALL_LAYOUT)
857 		gtk_widget_hide(summaryview->toggle_eventbox);
858 
859 	return summaryview;
860 }
861 
summary_relayout(SummaryView * summaryview)862 void summary_relayout(SummaryView *summaryview)
863 {
864 	gtk_widget_realize(summaryview->stat_box);
865 
866 	g_object_ref(summaryview->hbox_l);
867 	g_object_ref(summaryview->statlabel_msgs);
868 
869 	gtkut_container_remove(GTK_CONTAINER(
870 		gtk_widget_get_parent(summaryview->hbox_l)), summaryview->hbox_l);
871 	gtkut_container_remove(GTK_CONTAINER(
872 		gtk_widget_get_parent(summaryview->statlabel_msgs)), summaryview->statlabel_msgs);
873 
874 	switch (prefs_common.layout_mode) {
875 	case NORMAL_LAYOUT:
876 	case WIDE_LAYOUT:
877 	case WIDE_MSGLIST_LAYOUT:
878 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
879 		gtk_box_pack_end(GTK_BOX(summaryview->stat_box), summaryview->statlabel_msgs, FALSE, FALSE, 4);
880 		gtk_widget_show_all(summaryview->stat_box);
881 		gtk_widget_show_all(summaryview->stat_box2);
882 		if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
883 		    prefs_common.layout_mode == SMALL_LAYOUT)
884 			gtk_widget_hide(summaryview->toggle_eventbox);
885 		else
886 			gtk_widget_show(summaryview->toggle_eventbox);
887 		break;
888 	case VERTICAL_LAYOUT:
889 	case SMALL_LAYOUT:
890 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
891 		gtk_box_pack_start(GTK_BOX(summaryview->stat_box2), summaryview->statlabel_msgs, FALSE, FALSE, 4);
892 		gtk_widget_show_all(summaryview->stat_box);
893 		gtk_widget_show_all(summaryview->stat_box2);
894 		if (prefs_common.layout_mode == SMALL_LAYOUT) {
895 			gtk_widget_hide(summaryview->toggle_eventbox);
896 			gtk_widget_hide(summaryview->statlabel_msgs);
897 		} else {
898 			gtk_widget_show(summaryview->toggle_eventbox);
899 			gtk_widget_show(summaryview->statlabel_msgs);
900 		}
901 
902 		break;
903 	}
904 	summary_set_column_order(summaryview);
905 
906 	g_object_unref(summaryview->hbox_l);
907 	g_object_unref(summaryview->statlabel_msgs);
908 	quicksearch_relayout(summaryview->quicksearch);
909 	if (prefs_common.show_searchbar)
910 		quicksearch_show(summaryview->quicksearch);
911 	else
912 		quicksearch_hide(summaryview->quicksearch);
913 }
914 
summary_set_fonts(SummaryView * summaryview)915 static void summary_set_fonts(SummaryView *summaryview)
916 {
917 	PangoFontDescription *font_desc;
918 	gint size;
919 
920 	font_desc = pango_font_description_from_string(NORMAL_FONT);
921 	if (font_desc) {
922 		gtk_widget_modify_font(summaryview->ctree, font_desc);
923 		pango_font_description_free(font_desc);
924 	}
925 
926 	if (!bold_style) {
927 		bold_style = gtk_style_copy
928 			(gtk_widget_get_style(summaryview->ctree));
929 
930 		if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
931 			font_desc = pango_font_description_from_string(NORMAL_FONT);
932 			if (font_desc) {
933 				pango_font_description_free(bold_style->font_desc);
934 				bold_style->font_desc = font_desc;
935 			}
936 			pango_font_description_set_weight
937 					(bold_style->font_desc, PANGO_WEIGHT_BOLD);
938 		} else {
939 			font_desc = pango_font_description_from_string(BOLD_FONT);
940 			if (font_desc) {
941 				pango_font_description_free(bold_style->font_desc);
942 				bold_style->font_desc = font_desc;
943 			}
944 		}
945 		bold_marked_style = gtk_style_copy(bold_style);
946 		bold_marked_style->text[GTK_STATE_NORMAL] =
947 			summaryview->color_marked;
948 		bold_deleted_style = gtk_style_copy(bold_style);
949 		bold_deleted_style->text[GTK_STATE_NORMAL] =
950 			summaryview->color_dim;
951 	}
952 
953 	if (prefs_common.derive_from_normal_font || !SMALL_FONT) {
954 		font_desc = pango_font_description_new();
955 		size = pango_font_description_get_size
956 			(gtk_widget_get_style(summaryview->ctree)->font_desc);
957 		pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
958 	} else {
959 		font_desc = pango_font_description_from_string(SMALL_FONT);
960 	}
961 	if (font_desc) {
962 		gtk_widget_modify_font(summaryview->statlabel_folder, font_desc);
963 		gtk_widget_modify_font(summaryview->statlabel_select, font_desc);
964 		gtk_widget_modify_font(summaryview->statlabel_msgs, font_desc);
965 		pango_font_description_free(font_desc);
966 	}
967 
968 }
969 
summary_set_folder_pixmap(SummaryView * summaryview,StockPixmap icon)970 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon)
971 {
972 	GtkWidget *pixmap;
973 	if (!summaryview->folder_pixmap_eventbox) {
974 		summaryview->folder_pixmap_eventbox = gtk_event_box_new();
975 		gtk_widget_show(summaryview->folder_pixmap_eventbox);
976 		gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4);
977 		gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */
978 		g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event",
979 			 G_CALLBACK(summary_folder_eventbox_pressed),
980 			 summaryview);
981 	}
982 	if (summaryview->folder_pixmap)
983 		gtk_widget_destroy(summaryview->folder_pixmap);
984 
985 	pixmap = stock_pixmap_widget(icon);
986 	gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap);
987 	gtk_widget_show(pixmap);
988 	summaryview->folder_pixmap = pixmap;
989 }
990 
summary_init(SummaryView * summaryview)991 void summary_init(SummaryView *summaryview)
992 {
993 	GtkWidget *pixmap;
994 
995 	gtk_widget_realize(summaryview->ctree);
996 	stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
997 	stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
998 	stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
999 	stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
1000 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
1001 	stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
1002 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
1003 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
1004 	stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
1005 	stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
1006 	stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
1007 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
1008 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
1009 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
1010 	stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
1011 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
1012 	stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
1013 	stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
1014 	stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
1015 
1016 	summary_set_fonts(summaryview);
1017 
1018 	summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
1019 
1020 	pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
1021 	gtk_container_add (GTK_CONTAINER(summaryview->toggle_search), pixmap);
1022 	gtk_widget_show(pixmap);
1023 	summaryview->quick_search_pixmap = pixmap;
1024 
1025 #ifdef GENERIC_UMPC
1026 	pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
1027 	gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
1028 	gtk_widget_show(pixmap);
1029 	summaryview->multiple_sel_image = pixmap;
1030 #endif
1031 
1032 	/* Init summaryview prefs */
1033 	summaryview->sort_key = SORT_BY_NONE;
1034 	summaryview->sort_type = SORT_ASCENDING;
1035 
1036 	/* Init summaryview extra data */
1037 	summaryview->simplify_subject_preg = NULL;
1038 	summary_clear_list(summaryview);
1039 	summary_set_column_titles(summaryview);
1040 	summary_colorlabel_menu_create(summaryview, FALSE);
1041 	summary_tags_menu_create(summaryview, FALSE);
1042 	main_create_mailing_list_menu (summaryview->mainwin, NULL);
1043 	summary_set_menu_sensitive(summaryview);
1044 
1045 	summaryview->header_menu_lock = FALSE;
1046 }
1047 
1048 #define CURRENTLY_DISPLAYED(m) \
1049 ( (m->msgnum == displayed_msgnum) \
1050   && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
1051 
1052 #define FOLDER_SHOWS_TO_HDR(i) \
1053 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \
1054         || folder_has_parent_of_type(i, F_DRAFT) \
1055         || folder_has_parent_of_type(i, F_QUEUE)) )
1056 
summary_switch_from_to(SummaryView * summaryview,FolderItem * item)1057 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
1058 {
1059 	gboolean show_from = FALSE, show_to = FALSE;
1060 	gboolean showing_from = FALSE, showing_to = FALSE;
1061 	gint from_pos = 0, to_pos = 0;
1062 	SummaryColumnState *col_state = summaryview->col_state;
1063 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1064 
1065 	if (!item || ((prefs_common.layout_mode == VERTICAL_LAYOUT || prefs_common.layout_mode == SMALL_LAYOUT) && prefs_common.two_line_vert) )
1066 		return;
1067 	if (FOLDER_SHOWS_TO_HDR(item))
1068 		show_to = TRUE;
1069 	else
1070 		show_from = TRUE;
1071 
1072 	from_pos = summaryview->col_pos[S_COL_FROM];
1073 	to_pos = summaryview->col_pos[S_COL_TO];
1074 	showing_from = col_state[from_pos].visible;
1075 	showing_to = col_state[to_pos].visible;
1076 
1077 	if (showing_from && showing_to) {
1078 		debug_print("showing both\n");
1079 		return;
1080 	}
1081 
1082 	if (!showing_from && !showing_to) {
1083 		debug_print("showing none\n");
1084 		return;
1085 	}
1086 
1087 	debug_print("showing %s %s, must show %s %s\n",
1088 		showing_from?"From":"",
1089 		showing_to?"To":"",
1090 		show_from?"From":"",
1091 		show_to?"To":"");
1092 
1093 	if (showing_from == show_from && showing_to == show_to)
1094 		return;
1095 	/* else we'll switch both */
1096 
1097 	debug_print("switching columns\n");
1098 	col_state[from_pos].type = S_COL_TO;
1099 	col_state[from_pos].visible = show_to;
1100 
1101 	col_state[to_pos].type = S_COL_FROM;
1102 	col_state[to_pos].visible = show_from;
1103 
1104 	summaryview->col_pos[S_COL_TO] = from_pos;
1105 	summaryview->col_pos[S_COL_FROM] = to_pos;
1106 
1107 	gtk_cmclist_set_column_visibility
1108 		(GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible);
1109 	gtk_cmclist_set_column_visibility
1110 		(GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible);
1111 
1112 	summary_set_column_titles(summaryview);
1113 }
1114 
summaryview_reset_recursive_folder_match(SummaryView * summaryview)1115 static void summaryview_reset_recursive_folder_match(SummaryView *summaryview)
1116 {
1117 	GSList *cur;
1118 
1119 	for (cur = summaryview->recursive_matched_folders; cur != NULL; cur = cur->next) {
1120 		folderview_update_search_icon(cur->data, FALSE);
1121 	}
1122 
1123 	g_slist_free(summaryview->recursive_matched_folders);
1124 	summaryview->recursive_matched_folders = NULL;
1125 	summaryview->search_root_folder = NULL;
1126 }
1127 
summaryview_quicksearch_recursive_progress(gpointer data,guint at,guint matched,guint total)1128 static gboolean summaryview_quicksearch_recursive_progress(gpointer data, guint at, guint matched, guint total)
1129 {
1130 	QuickSearch *search = (QuickSearch*) data;
1131 	gint interval = quicksearch_is_fast(search) ? 5000 : 100;
1132 
1133 	statusbar_progress_all(at, total, interval);
1134 	if (at % interval == 0)
1135 		GTK_EVENTS_FLUSH();
1136 
1137 	if (matched > 0)
1138 		return FALSE;
1139 
1140 	return TRUE;
1141 }
1142 
summaryview_quicksearch_recurse_step(SummaryView * summaryview,FolderItem * item)1143 static void summaryview_quicksearch_recurse_step(SummaryView *summaryview, FolderItem *item)
1144 {
1145 	MsgInfoList *msgs = NULL;
1146 	gboolean result = TRUE;
1147 
1148 	statusbar_print_all(_("Searching in %s... \n"),
1149 		item->path ? item->path : "(null)");
1150 	folder_item_update_freeze();
1151 
1152 	quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_quicksearch_recursive_progress, summaryview->quicksearch);
1153 	if (!quicksearch_run_on_folder(summaryview->quicksearch, item, &msgs))
1154 		result = FALSE;
1155 
1156 	result = result && msgs != NULL;
1157 
1158 	if (msgs != NULL)
1159 		procmsg_msg_list_free(msgs);
1160 
1161 	folder_item_update_thaw();
1162 	statusbar_progress_all(0, 0, 0);
1163 	statusbar_pop_all();
1164 
1165 	if (result) {
1166 		summaryview->recursive_matched_folders = g_slist_prepend(
1167 				summaryview->recursive_matched_folders, item);
1168 
1169 		folderview_update_search_icon(item, TRUE);
1170 	}
1171 }
1172 
summaryview_quicksearch_search_subfolders(SummaryView * summaryview,FolderItem * folder_item)1173 static void summaryview_quicksearch_search_subfolders(SummaryView *summaryview, FolderItem *folder_item)
1174 {
1175 	FolderItem *cur = NULL;
1176 	GNode *node = folder_item->node->children;
1177 
1178 	if (!prefs_common.summary_quicksearch_recurse
1179 			|| !quicksearch_has_sat_predicate(summaryview->quicksearch)
1180 			|| quicksearch_is_in_typing(summaryview->quicksearch))
1181 		return;
1182 
1183 	for (; node != NULL; node = node->next) {
1184 		if (!quicksearch_has_sat_predicate(summaryview->quicksearch))
1185 			return;
1186 
1187 		cur = FOLDER_ITEM(node->data);
1188 		summaryview_quicksearch_recurse_step(summaryview, cur);
1189 		if (cur->node->children)
1190 			summaryview_quicksearch_search_subfolders(summaryview, cur);
1191 	}
1192 }
1193 
summaryview_quicksearch_recurse(SummaryView * summaryview)1194 static void summaryview_quicksearch_recurse(SummaryView *summaryview)
1195 {
1196 	if (!prefs_common.summary_quicksearch_recurse
1197 		|| !quicksearch_has_sat_predicate(summaryview->quicksearch)
1198 		|| summaryview->folder_item == NULL) {
1199 		return;
1200 	}
1201 	START_TIMING("");
1202 	main_window_cursor_wait(summaryview->mainwin);
1203 
1204 	summaryview_reset_recursive_folder_match(summaryview);
1205 	summaryview->search_root_folder = summaryview->folder_item;
1206 
1207 	summaryview_quicksearch_search_subfolders(summaryview, summaryview->folder_item);
1208 
1209 	main_window_cursor_normal(summaryview->mainwin);
1210 	END_TIMING();
1211 }
1212 
summary_check_consistency(FolderItem * item,GSList * mlist)1213 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
1214 {
1215 	int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0;
1216 	GSList *cur;
1217 	START_TIMING("");
1218 	for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1219 		MsgInfo * msginfo = (MsgInfo *) cur->data;
1220 		t++;
1221 		if (MSG_IS_NEW(msginfo->flags))
1222 			n++;
1223 		if (MSG_IS_UNREAD(msginfo->flags))
1224 			u++;
1225 		if (MSG_IS_MARKED(msginfo->flags))
1226 			m++;
1227 		if (MSG_IS_REPLIED(msginfo->flags))
1228 			r++;
1229 		if (MSG_IS_FORWARDED(msginfo->flags))
1230 			f++;
1231 		if (MSG_IS_LOCKED(msginfo->flags))
1232 			l++;
1233 		if (MSG_IS_IGNORE_THREAD(msginfo->flags))
1234 			i++;
1235 		if (MSG_IS_WATCH_THREAD(msginfo->flags))
1236 			w++;
1237 	}
1238 	if (t != item->total_msgs
1239 	||  n != item->new_msgs
1240 	||  u != item->unread_msgs
1241 	||  m != item->marked_msgs
1242 	||  r != item->replied_msgs
1243 	||  f != item->forwarded_msgs
1244 	||  l != item->locked_msgs
1245 	||  i != item->ignored_msgs
1246 	||  w != item->watched_msgs
1247 	||  (m == 0 && item->unreadmarked_msgs != 0)
1248 	||  item->unreadmarked_msgs < 0) {
1249 		debug_print("Inconsistency\n");
1250 		folder_item_scan_full(item, FALSE);
1251 		END_TIMING();
1252 		return FALSE;
1253 	}
1254 	END_TIMING();
1255 	return TRUE;
1256 }
1257 
summaryview_search_root_progress(gpointer data,guint at,guint matched,guint total)1258 gboolean summaryview_search_root_progress(gpointer data, guint at, guint matched, guint total)
1259 {
1260 	SummaryView *summaryview = (SummaryView*) data;
1261 
1262 	gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000 : 100;
1263 
1264 	statusbar_progress_all(at, total, interval);
1265 
1266 	if (at % interval == 0)
1267 		GTK_EVENTS_FLUSH();
1268 
1269 	return TRUE;
1270 }
1271 
summary_show(SummaryView * summaryview,FolderItem * item,gboolean avoid_refresh)1272 gboolean summary_show(SummaryView *summaryview, FolderItem *item, gboolean avoid_refresh)
1273 {
1274 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1275 	GtkCMCTreeNode *node = NULL;
1276 	GSList *mlist = NULL;
1277 	gchar *buf;
1278 	gboolean is_refresh;
1279 	guint selected_msgnum = 0;
1280 	guint displayed_msgnum = 0;
1281 	GSList *cur;
1282         GSList *not_killed;
1283 	gboolean hidden_removed = FALSE;
1284 
1285 	if (summary_is_locked(summaryview)) return FALSE;
1286 
1287 	if (!summaryview->mainwin)
1288 		return FALSE;
1289 	START_TIMING("");
1290 	summary_switch_from_to(summaryview, item);
1291 
1292 	inc_lock();
1293 	summary_lock(summaryview);
1294 
1295 	menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
1296 
1297 	utils_free_regex();
1298 
1299 	is_refresh = (item == summaryview->folder_item && !avoid_refresh) ? TRUE : FALSE;
1300 
1301 	if (item && item->folder->klass->item_opened) {
1302 		item->folder->klass->item_opened(item);
1303 	}
1304 
1305 	if (!is_refresh) {
1306 		main_create_mailing_list_menu (summaryview->mainwin, NULL);
1307 		if (prefs_common.layout_mode == SMALL_LAYOUT) {
1308 			if (item) {
1309 				mainwindow_enter_folder(summaryview->mainwin);
1310 				gtk_widget_grab_focus(summaryview->ctree);
1311 			}
1312 		}
1313 	}
1314 	if (!prefs_common.summary_quicksearch_sticky
1315 	 && (!prefs_common.summary_quicksearch_recurse
1316 	  || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1317 	  || (item && !folder_is_child_of(item, summaryview->search_root_folder)))
1318 	 && !quicksearch_is_running(summaryview->quicksearch)
1319 	 && !is_refresh) {
1320 		quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
1321 	}
1322 
1323 	/* STATUSBAR_POP(summaryview->mainwin); */
1324 
1325 	if (is_refresh) {
1326 		selected_msgnum = summary_get_msgnum(summaryview,
1327 						     summaryview->selected);
1328 		displayed_msgnum = summary_get_msgnum(summaryview,
1329 						      summaryview->displayed);
1330 	}
1331 
1332 	/* process the marks if any */
1333 	if (!is_refresh &&
1334 			(summaryview->mainwin->lock_count == 0 &&
1335 			 (summaryview->moved > 0 || summaryview->copied > 0))) {
1336 		AlertValue val;
1337 		gboolean changed = FALSE;
1338 
1339 		val = alertpanel(_("Process mark"),
1340 				 _("Some marks are left. Process them?"),
1341 				 GTK_STOCK_NO, GTK_STOCK_YES, GTK_STOCK_CANCEL, ALERTFOCUS_FIRST);
1342 		if (G_ALERTALTERNATE == val) {
1343 			summary_unlock(summaryview);
1344 			summary_execute(summaryview);
1345 			summary_lock(summaryview);
1346 			changed = TRUE;
1347 		} else if (G_ALERTDEFAULT == val) {
1348 			/* DO NOTHING */
1349 		} else {
1350 			summary_unlock(summaryview);
1351 			inc_unlock();
1352 			END_TIMING();
1353 			return FALSE;
1354 		}
1355 		if (changed || !quicksearch_has_sat_predicate(summaryview->quicksearch))
1356 			folder_update_op_count();
1357 	}
1358 
1359 	summary_freeze(summaryview);
1360 
1361 	summary_clear_list(summaryview);
1362 
1363 	buf = NULL;
1364 	if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
1365 		g_free(buf);
1366 		debug_print("empty folder (%p %s %p %d)\n",
1367 				item,
1368 				(item && item->path)?item->path:"(null)",
1369 				item?folder_item_parent(item):0x0,
1370 				item?item->no_select:FALSE);
1371 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE);
1372 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE);
1373 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE);
1374 		summary_clear_all(summaryview);
1375 		summaryview->folder_item = item;
1376 		summary_thaw(summaryview);
1377 		summary_unlock(summaryview);
1378 		inc_unlock();
1379 		END_TIMING();
1380 		return TRUE;
1381 	}
1382 	g_free(buf);
1383 
1384 	if (!is_refresh)
1385 		messageview_clear(summaryview->messageview);
1386 
1387 	summaryview->folder_item = item;
1388 	item->opened = TRUE;
1389 
1390 	buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
1391 	debug_print("%s\n", buf);
1392 	STATUSBAR_PUSH(summaryview->mainwin, buf);
1393 	g_free(buf);
1394 
1395 	main_window_cursor_wait(summaryview->mainwin);
1396 
1397 	mlist = folder_item_get_msg_list(item);
1398 
1399 	if (!summary_check_consistency(item, mlist)) {
1400 		debug_print("reloading due to inconsistency\n");
1401 		procmsg_msg_list_free(mlist);
1402 		mlist = folder_item_get_msg_list(item);
1403 	}
1404 
1405 	if (quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1406 		procmsg_msg_list_free(mlist);
1407 		mlist = NULL;
1408 
1409 		START_TIMING("quicksearch");
1410 
1411 		statusbar_print_all(_("Searching in %s... \n"),
1412 			summaryview->folder_item->path ?
1413 			summaryview->folder_item->path : "(null)");
1414 
1415 		folder_item_update_freeze();
1416 
1417 		quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_search_root_progress, summaryview);
1418 		quicksearch_run_on_folder(summaryview->quicksearch, summaryview->folder_item, &mlist);
1419 
1420 		folder_item_update_thaw();
1421 		statusbar_progress_all(0, 0, 0);
1422 		statusbar_pop_all();
1423 
1424 		if (!quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1425 			debug_print("search cancelled!\n");
1426 			summary_thaw(summaryview);
1427 			STATUSBAR_POP(summaryview->mainwin);
1428 			main_window_cursor_normal(summaryview->mainwin);
1429 			summary_unlock(summaryview);
1430 			inc_unlock();
1431 			summary_show(summaryview, summaryview->folder_item, FALSE);
1432 			END_TIMING();
1433 			return FALSE;
1434 		}
1435 		END_TIMING();
1436 	}
1437 
1438 	if ((summaryview->folder_item->hide_read_msgs
1439              || summaryview->folder_item->hide_del_msgs
1440              || summaryview->folder_item->hide_read_threads) &&
1441 	    quicksearch_has_sat_predicate(summaryview->quicksearch) == FALSE) {
1442 		GSList *not_killed;
1443 
1444 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1445 						summaryview->folder_item->hide_read_msgs);
1446 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1447 						summaryview->folder_item->hide_del_msgs);
1448 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1449 						summaryview->folder_item->hide_read_threads);
1450 		not_killed = NULL;
1451 		for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1452 			MsgInfo * msginfo = (MsgInfo *) cur->data;
1453 
1454 			if (!msginfo->hidden) {
1455 				if (MSG_IS_DELETED(msginfo->flags) && summaryview->folder_item->hide_del_msgs) {
1456 					procmsg_msginfo_free(&msginfo);
1457 					continue;
1458 				}
1459 				if (summaryview->folder_item->hide_read_msgs) {
1460 					if (MSG_IS_UNREAD(msginfo->flags) &&
1461 					    !MSG_IS_IGNORE_THREAD(msginfo->flags))
1462 						not_killed = g_slist_prepend(not_killed, msginfo);
1463 					else if (MSG_IS_MARKED(msginfo->flags) ||
1464 						 MSG_IS_LOCKED(msginfo->flags))
1465 						not_killed = g_slist_prepend(not_killed, msginfo);
1466 					else if (is_refresh &&
1467 						(msginfo->msgnum == selected_msgnum ||
1468 						 msginfo->msgnum == displayed_msgnum))
1469 						not_killed = g_slist_prepend(not_killed, msginfo);
1470 					else
1471 						procmsg_msginfo_free(&msginfo);
1472 				} else {
1473 					not_killed = g_slist_prepend(not_killed, msginfo);
1474 				}
1475 			 } else
1476 			 	procmsg_msginfo_free(&msginfo);
1477 		}
1478 		hidden_removed = TRUE;
1479 		g_slist_free(mlist);
1480 		mlist = not_killed;
1481 	} else {
1482 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1483 						FALSE);
1484 		summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1485 						FALSE);
1486 		summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1487 						FALSE);
1488 	}
1489 
1490 	if (!hidden_removed) {
1491 		START_TIMING("removing hidden");
1492         	not_killed = NULL;
1493         	for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1494                 	MsgInfo * msginfo = (MsgInfo *) cur->data;
1495 
1496                 	if (!msginfo->hidden)
1497                         	not_killed = g_slist_prepend(not_killed, msginfo);
1498                 	else
1499                         	procmsg_msginfo_free(&msginfo);
1500         	}
1501 		g_slist_free(mlist);
1502 		mlist = not_killed;
1503 		END_TIMING();
1504 	}
1505 
1506 	STATUSBAR_POP(summaryview->mainwin);
1507 
1508 	/* set ctree and hash table from the msginfo list, and
1509 	   create the thread */
1510 	summary_set_ctree_from_list(summaryview, mlist, selected_msgnum);
1511 
1512 	g_slist_free(mlist);
1513 
1514 	if (is_refresh) {
1515 		if (!quicksearch_is_in_typing(summaryview->quicksearch)) {
1516 			summaryview->displayed =
1517 				summary_find_msg_by_msgnum(summaryview,
1518 							   displayed_msgnum);
1519 			if (!summaryview->displayed)
1520 				messageview_clear(summaryview->messageview);
1521 			summary_unlock(summaryview);
1522 
1523 			if (quicksearch_is_running(summaryview->quicksearch))
1524 				summary_select_by_msgnum(summaryview, selected_msgnum,
1525 						OPEN_SELECTED_ON_SEARCH_RESULTS);
1526 			else
1527 				summary_select_by_msgnum(summaryview, selected_msgnum,
1528 						FALSE);
1529 
1530 			summary_lock(summaryview);
1531 			if (!summaryview->selected) {
1532 				/* no selected message - select first unread
1533 				   message, but do not display it */
1534 				node = summary_find_next_flagged_msg(summaryview, NULL,
1535 								     MSG_UNREAD, FALSE);
1536 				if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL)
1537 					node = gtk_cmctree_node_nth
1538 						(ctree,
1539 						 item->sort_type == SORT_DESCENDING
1540 						 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1541 				summary_unlock(summaryview);
1542 
1543 				if (quicksearch_is_running(summaryview->quicksearch))
1544 					summary_select_node(summaryview, node,
1545 							OPEN_SELECTED_ON_SEARCH_RESULTS);
1546 				else
1547 					summary_select_node(summaryview, node,
1548 							OPEN_SELECTED_ON_FOLDER_OPEN);
1549 
1550 				summary_lock(summaryview);
1551 			}
1552 		} else {
1553 			/* just select first/last */
1554 			if (GTK_CMCLIST(ctree)->row_list != NULL)
1555 				node = gtk_cmctree_node_nth
1556 					(ctree,
1557 					 item->sort_type == SORT_DESCENDING
1558 					 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1559 			summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
1560 		}
1561 	} else {
1562 		/* backward compat */
1563 		int i = 0;
1564 		gboolean set = FALSE, stop = FALSE;
1565 		for (i = 0; i < 6; i++) {
1566 			EntryAction act = prefs_common.summary_select_prio[i];
1567 
1568 			if (act != ACTION_UNSET) {
1569 				set = TRUE;
1570 				break;
1571 			}
1572 		}
1573 		if (!set)
1574 			prefs_summary_open_set_defaults();
1575 
1576  		for (i = 0; i < 6 && node == NULL; i++) {
1577 			EntryAction act = prefs_common.summary_select_prio[i];
1578 
1579 			switch(act) {
1580 			case ACTION_OLDEST_MARKED:
1581 				if (summaryview->sort_type == SORT_ASCENDING)
1582 					node = summary_find_next_flagged_msg(summaryview, NULL,
1583 					     MSG_MARKED, FALSE);
1584 				else
1585 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1586 					     MSG_MARKED, FALSE);
1587 				break;
1588 			case ACTION_NEWEST_MARKED:
1589 				if (summaryview->sort_type == SORT_ASCENDING)
1590 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1591 					     MSG_MARKED, FALSE);
1592 				else
1593 					node = summary_find_next_flagged_msg(summaryview, NULL,
1594 					     MSG_MARKED, FALSE);
1595 				break;
1596 			case ACTION_OLDEST_NEW:
1597 				if (summaryview->sort_type == SORT_ASCENDING)
1598 					node = summary_find_next_flagged_msg(summaryview, NULL,
1599 					     MSG_NEW, FALSE);
1600 				else
1601 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1602 					     MSG_NEW, FALSE);
1603 				break;
1604 			case ACTION_NEWEST_NEW:
1605 				if (summaryview->sort_type == SORT_ASCENDING)
1606 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1607 					     MSG_NEW, FALSE);
1608 				else
1609 					node = summary_find_next_flagged_msg(summaryview, NULL,
1610 					     MSG_NEW, FALSE);
1611 				break;
1612 			case ACTION_OLDEST_UNREAD:
1613 				if (summaryview->sort_type == SORT_ASCENDING)
1614 					node = summary_find_next_flagged_msg(summaryview, NULL,
1615 					     MSG_UNREAD, FALSE);
1616 				else
1617 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1618 					     MSG_UNREAD, FALSE);
1619 				break;
1620 			case ACTION_NEWEST_UNREAD:
1621 				if (summaryview->sort_type == SORT_ASCENDING)
1622 					node = summary_find_prev_flagged_msg(summaryview, NULL,
1623 					     MSG_UNREAD, FALSE);
1624 				else
1625 					node = summary_find_next_flagged_msg(summaryview, NULL,
1626 					     MSG_UNREAD, FALSE);
1627 				break;
1628 			case ACTION_LAST_OPENED:
1629 				if (summaryview->folder_item) {
1630 					node = summary_find_msg_by_msgnum(summaryview,
1631 							summaryview->folder_item->last_seen);
1632 				}
1633 				break;
1634 			case ACTION_NEWEST_LIST:
1635 				if (GTK_CMCLIST(ctree)->row_list != NULL) {
1636 					node = gtk_cmctree_node_nth
1637 						(ctree,
1638 						 item->sort_type == SORT_DESCENDING
1639 						 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1640 				}
1641 				break;
1642 			case ACTION_OLDEST_LIST:
1643 				if (GTK_CMCLIST(ctree)->row_list != NULL) {
1644 					node = gtk_cmctree_node_nth
1645 						(ctree,
1646 						 item->sort_type == SORT_ASCENDING
1647 						 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1648 				}
1649 				break;
1650 			case ACTION_NOTHING:
1651 			case ACTION_UNSET:
1652 				node = NULL;
1653 				stop = TRUE;
1654 				break;
1655 			}
1656 
1657 			if (stop || node)
1658 				break;
1659 		}
1660 
1661 		summary_unlock(summaryview);
1662 
1663 		if (node) {
1664 			gint open_selected = -1;
1665 			if (!is_refresh) {
1666 				if (OPEN_SELECTED_ON_FOLDER_OPEN)
1667 					open_selected = 1;
1668 				else
1669 					open_selected = 0;
1670 			}
1671 			summary_select_node(summaryview, node, open_selected);
1672 		}
1673 
1674 		summary_lock(summaryview);
1675 	}
1676 
1677 	summary_status_show(summaryview);
1678 	summary_set_menu_sensitive(summaryview);
1679 	toolbar_main_set_sensitive(summaryview->mainwin);
1680 
1681 	summary_thaw(summaryview);
1682 	debug_print("\n");
1683 	STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
1684 	STATUSBAR_POP(summaryview->mainwin);
1685 	main_window_cursor_normal(summaryview->mainwin);
1686 	summary_unlock(summaryview);
1687 	inc_unlock();
1688 	END_TIMING();
1689 	return TRUE;
1690 }
1691 
1692 #undef CURRENTLY_DISPLAYED
1693 
summary_cancel_mark_read_timeout(SummaryView * summaryview)1694 static void summary_cancel_mark_read_timeout(SummaryView *summaryview) {
1695 	if (summaryview->mark_as_read_timeout_tag != 0) {
1696 		g_source_remove(summaryview->mark_as_read_timeout_tag);
1697 		summaryview->mark_as_read_timeout_tag = 0;
1698 	}
1699 }
1700 
summary_clear_list(SummaryView * summaryview)1701 void summary_clear_list(SummaryView *summaryview)
1702 {
1703 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1704 	gint optimal_width;
1705 
1706 	summary_freeze(summaryview);
1707 
1708 	gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree),
1709 				NULL, summary_free_msginfo_func, NULL);
1710 
1711 	if (summaryview->folder_item) {
1712 		summaryview->folder_item->opened = FALSE;
1713 		summaryview->folder_item = NULL;
1714 	}
1715 
1716 	summary_cancel_mark_read_timeout(summaryview);
1717 
1718 	summaryview->display_msg = FALSE;
1719 
1720 	summaryview->selected = NULL;
1721 	summaryview->displayed = NULL;
1722 	summaryview->total_size = 0;
1723 	summaryview->deleted = summaryview->moved = 0;
1724 	summaryview->copied = 0;
1725 	if (summaryview->msgid_table) {
1726 		g_hash_table_destroy(summaryview->msgid_table);
1727 		summaryview->msgid_table = NULL;
1728 	}
1729 	if (summaryview->subject_table) {
1730 		g_hash_table_destroy(summaryview->subject_table);
1731 		summaryview->subject_table = NULL;
1732 	}
1733 	summaryview->mlist = NULL;
1734 
1735 	gtk_cmclist_clear(clist);
1736 	if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1737 		optimal_width = gtk_cmclist_optimal_column_width
1738 			(clist, summaryview->col_pos[S_COL_SUBJECT]);
1739 		gtk_cmclist_set_column_width
1740 			(clist, summaryview->col_pos[S_COL_SUBJECT],
1741 			 optimal_width);
1742 	}
1743 
1744 	summary_thaw(summaryview);
1745 }
1746 
summary_clear_all(SummaryView * summaryview)1747 void summary_clear_all(SummaryView *summaryview)
1748 {
1749 	messageview_clear(summaryview->messageview);
1750 	summary_clear_list(summaryview);
1751 	summary_set_menu_sensitive(summaryview);
1752 	toolbar_main_set_sensitive(summaryview->mainwin);
1753 	summary_status_show(summaryview);
1754 }
1755 
summary_lock(SummaryView * summaryview)1756 void summary_lock(SummaryView *summaryview)
1757 {
1758 	summaryview->lock_count++;
1759 }
1760 
summary_unlock(SummaryView * summaryview)1761 void summary_unlock(SummaryView *summaryview)
1762 {
1763 	if (summaryview->lock_count)
1764 		summaryview->lock_count--;
1765 }
1766 
summary_is_locked(SummaryView * summaryview)1767 gboolean summary_is_locked(SummaryView *summaryview)
1768 {
1769 	return summaryview->lock_count > 0;
1770 }
1771 
summary_get_selection_type(SummaryView * summaryview)1772 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1773 {
1774 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1775 	SummarySelection selection;
1776 
1777 	if (!clist->row_list)
1778 		selection = SUMMARY_NONE;
1779 	else if (!clist->selection)
1780 		selection = SUMMARY_SELECTED_NONE;
1781 	else if (!clist->selection->next)
1782 		selection = SUMMARY_SELECTED_SINGLE;
1783 	else
1784 		selection = SUMMARY_SELECTED_MULTIPLE;
1785 
1786 	return selection;
1787 }
1788 
1789 /*!
1790  *\return	MsgInfo	* Selected message if there's one selected;
1791  *		if multiple selected, or none, return NULL.
1792  */
summary_get_selected_msg(SummaryView * summaryview)1793 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
1794 {
1795 	/* summaryview->selected may be valid when multiple
1796 	 * messages were selected */
1797 	GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection;
1798 
1799 	if (sellist == NULL || sellist->next)
1800 		return NULL;
1801 
1802 	return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
1803 }
1804 
summary_get_selected_msg_list(SummaryView * summaryview)1805 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1806 {
1807 	GSList *mlist = NULL;
1808 	GList *cur;
1809 	MsgInfo *msginfo;
1810 
1811 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
1812 	     cur = cur->next) {
1813 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
1814 		mlist = g_slist_prepend(mlist,
1815 				procmsg_msginfo_new_ref(msginfo));
1816 	}
1817 
1818 	mlist = g_slist_reverse(mlist);
1819 
1820 	return mlist;
1821 }
1822 
summary_set_menu_sensitive(SummaryView * summaryview)1823 void summary_set_menu_sensitive(SummaryView *summaryview)
1824 {
1825 	SensitiveCondMask state;
1826 
1827 	main_window_set_menu_sensitive(summaryview->mainwin);
1828 
1829 	state = main_window_get_current_state(summaryview->mainwin);
1830 
1831 #define SET_SENSITIVE(entry_str, ...) \
1832 { \
1833 	SensitiveCondMask cond = main_window_get_mask(__VA_ARGS__, -1); \
1834 	cm_menu_set_sensitive_full(summaryview->mainwin->ui_manager, \
1835 			(const gchar *) entry_str, \
1836 			((cond & state) == cond)); \
1837 }
1838 	SET_SENSITIVE("Menus/SummaryViewPopup/Reedit", M_ALLOW_REEDIT);
1839 	SET_SENSITIVE("Menus/SummaryViewPopup/Reply", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1840 #ifndef GENERIC_UMPC
1841 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1842 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/All", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1843 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/Sender", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1844 	SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/MailingList", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1845 #endif
1846 
1847 	SET_SENSITIVE("Menus/SummaryViewPopup/Forward", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1848 #ifndef GENERIC_UMPC
1849 	SET_SENSITIVE("Menus/SummaryViewPopup/ForwardAtt", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1850 	SET_SENSITIVE("Menus/SummaryViewPopup/Redirect", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1851 #endif
1852 
1853 	SET_SENSITIVE("Menus/SummaryViewPopup/Move", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS);
1854 	SET_SENSITIVE("Menus/SummaryViewPopup/Copy", M_TARGET_EXIST, M_EXEC);
1855 	SET_SENSITIVE("Menus/SummaryViewPopup/Trash", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS, M_NOT_TRASH);
1856 #ifndef GENERIC_UMPC
1857 	SET_SENSITIVE("Menus/SummaryViewPopup/Delete", M_TARGET_EXIST, M_ALLOW_DELETE);
1858 #endif
1859 
1860 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark", M_TARGET_EXIST);
1861 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Mark", M_TARGET_EXIST);
1862 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Unmark", M_TARGET_EXIST);
1863 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkRead", M_TARGET_EXIST);
1864 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkUnread", M_TARGET_EXIST);
1865 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/IgnoreThread", M_TARGET_EXIST);
1866 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/UnignoreThread", M_TARGET_EXIST);
1867 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/WatchThread", M_TARGET_EXIST);
1868 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/UnwatchThread", M_TARGET_EXIST);
1869 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Lock", M_TARGET_EXIST);
1870 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Unlock", M_TARGET_EXIST);
1871 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkSpam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1872 	SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkHam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1873 	SET_SENSITIVE("Menus/SummaryViewPopup/ColorLabel", M_TARGET_EXIST);
1874 	SET_SENSITIVE("Menus/SummaryViewPopup/Tags", M_TARGET_EXIST);
1875 
1876 #ifndef GENERIC_UMPC
1877 	SET_SENSITIVE("Menus/SummaryViewPopup/AddSenderToAB", M_SINGLE_TARGET_EXIST);
1878 #endif
1879 	SET_SENSITIVE("Menus/SummaryViewPopup/CreateFilterRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1880 #ifndef GENERIC_UMPC
1881 	SET_SENSITIVE("Menus/SummaryViewPopup/CreateProcessingRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1882 #endif
1883 
1884 	SET_SENSITIVE("Menus/SummaryViewPopup/View", M_SINGLE_TARGET_EXIST);
1885 	SET_SENSITIVE("Menus/SummaryViewPopup/View/OpenNewWindow", M_SINGLE_TARGET_EXIST);
1886 	SET_SENSITIVE("Menus/SummaryViewPopup/View/MessageSource", M_SINGLE_TARGET_EXIST);
1887 #ifndef GENERIC_UMPC
1888 	SET_SENSITIVE("Menus/SummaryViewPopup/View/AllHeaders", M_SINGLE_TARGET_EXIST);
1889 #endif
1890 	SET_SENSITIVE("Menus/SummaryViewPopup/SaveAs", M_TARGET_EXIST);
1891 #ifndef GENERIC_UMPC
1892 	SET_SENSITIVE("Menus/SummaryViewPopup/Print", M_TARGET_EXIST);
1893 #endif
1894 #undef SET_SENSITIVE
1895 
1896 	summary_lock(summaryview);
1897 #ifndef GENERIC_UMPC
1898 	if (summaryview->messageview
1899 	&&  summaryview->messageview->mimeview
1900 	&&  summaryview->messageview->mimeview->textview)
1901 		cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, "Menus/SummaryViewPopup/View/AllHeaders",
1902 			prefs_common.show_all_headers);
1903 #endif
1904 	summary_unlock(summaryview);
1905 }
summary_select_prev(SummaryView * summaryview)1906 void summary_select_prev(SummaryView *summaryview)
1907 {
1908 	GtkCMCTreeNode *node = summaryview->selected;
1909 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1910 
1911 	if (summaryview->sort_type == SORT_ASCENDING)
1912 		node = gtkut_ctree_node_prev(ctree, node);
1913 	else
1914 		node = gtkut_ctree_node_next(ctree, node);
1915 
1916 	if (node && node != summaryview->selected)
1917 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1918 }
1919 
summary_select_next(SummaryView * summaryview)1920 void summary_select_next(SummaryView *summaryview)
1921 {
1922 	GtkCMCTreeNode *node = summaryview->selected;
1923 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1924 
1925 	if (summaryview->sort_type == SORT_ASCENDING)
1926 		node = gtkut_ctree_node_next(ctree, node);
1927 	else
1928 		node = gtkut_ctree_node_prev(ctree, node);
1929 
1930 	if (node && node != summaryview->selected)
1931 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1932 }
1933 
summary_select_prev_unread(SummaryView * summaryview)1934 void summary_select_prev_unread(SummaryView *summaryview)
1935 {
1936 	GtkCMCTreeNode *node;
1937 
1938 	if (summaryview->sort_type == SORT_ASCENDING)
1939 		node = summary_find_prev_flagged_msg
1940 			(summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1941 	else
1942 		node = summary_find_next_flagged_msg
1943 			(summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1944 
1945 	if (!node || node == summaryview->selected) {
1946 		AlertValue val = 0;
1947 
1948  		switch (prefs_common.next_unread_msg_dialog) {
1949  			case NEXTUNREADMSGDIALOG_ALWAYS:
1950 				val = alertpanel(_("No more unread messages"),
1951 						 _("No unread message found. "
1952 						   "Search from the end?"),
1953 						 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
1954  				break;
1955  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
1956  				val = G_ALERTALTERNATE;
1957  				break;
1958  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
1959  				val = !G_ALERTALTERNATE;
1960  				break;
1961  			default:
1962  				debug_print(
1963  					_("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1964  		}
1965 		if (val != G_ALERTALTERNATE) return;
1966 		if (summaryview->sort_type == SORT_ASCENDING)
1967 			node = summary_find_prev_flagged_msg(summaryview, NULL,
1968 							     MSG_UNREAD, FALSE);
1969 		else
1970 			node = summary_find_next_flagged_msg(summaryview, NULL,
1971 							     MSG_UNREAD, FALSE);
1972 	}
1973 
1974 	if (!node)
1975 		alertpanel_notice(_("No unread messages."));
1976 	else
1977 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1978 }
1979 
summary_select_next_unread(SummaryView * summaryview)1980 void summary_select_next_unread(SummaryView *summaryview)
1981 {
1982 	GtkCMCTreeNode *node = summaryview->selected;
1983 
1984 	if (summaryview->sort_type == SORT_ASCENDING)
1985 		node = summary_find_next_flagged_msg
1986 			(summaryview, node, MSG_UNREAD, TRUE);
1987 	else
1988 		node = summary_find_prev_flagged_msg
1989 			(summaryview, node, MSG_UNREAD, TRUE);
1990 
1991 	if (node)
1992 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1993 	else {
1994 		AlertValue val = 0;
1995 
1996  		switch (prefs_common.next_unread_msg_dialog) {
1997  			case NEXTUNREADMSGDIALOG_ALWAYS:
1998 				val = alertpanel(_("No more unread messages"),
1999 						 _("No unread message found. "
2000 						   "Go to next folder?"),
2001 						 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2002  				break;
2003  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
2004  				val = G_ALERTALTERNATE;
2005  				break;
2006  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
2007  				val = G_ALERTOTHER;
2008  				break;
2009  			default:
2010  				debug_print(
2011  					_("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2012  		}
2013 
2014 		if (val == G_ALERTALTERNATE)
2015 			folderview_select_next_with_flag(summaryview->folderview, MSG_UNREAD);
2016 	}
2017 }
2018 
summary_select_prev_new(SummaryView * summaryview)2019 void summary_select_prev_new(SummaryView *summaryview)
2020 {
2021 	GtkCMCTreeNode *node;
2022 
2023 	if (summaryview->sort_type == SORT_ASCENDING)
2024 		node = summary_find_prev_flagged_msg
2025 			(summaryview, summaryview->selected, MSG_NEW, TRUE);
2026 	else
2027 		node = summary_find_next_flagged_msg
2028 			(summaryview, summaryview->selected, MSG_NEW, TRUE);
2029 
2030 	if (!node || node == summaryview->selected) {
2031 		AlertValue val = 0;
2032 
2033  		switch (prefs_common.next_unread_msg_dialog) {
2034  			case NEXTUNREADMSGDIALOG_ALWAYS:
2035 				val = alertpanel(_("No more new messages"),
2036 						 _("No new message found. "
2037 						   "Search from the end?"),
2038 						 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2039  				break;
2040  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
2041  				val = G_ALERTALTERNATE;
2042  				break;
2043  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
2044  				val = !G_ALERTALTERNATE;
2045  				break;
2046  			default:
2047  				debug_print(
2048  					_("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2049  		}
2050 		if (val != G_ALERTALTERNATE) return;
2051 		if (summaryview->sort_type == SORT_ASCENDING)
2052 			node = summary_find_prev_flagged_msg(summaryview, NULL,
2053 						     	     MSG_NEW, FALSE);
2054 		else
2055 			node = summary_find_next_flagged_msg(summaryview, NULL,
2056 							     MSG_NEW, FALSE);
2057 	}
2058 
2059 	if (!node)
2060 		alertpanel_notice(_("No new messages."));
2061 	else
2062 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2063 }
2064 
summary_select_next_new(SummaryView * summaryview)2065 void summary_select_next_new(SummaryView *summaryview)
2066 {
2067 	GtkCMCTreeNode *node = summaryview->selected;
2068 
2069 	if (summaryview->sort_type == SORT_ASCENDING)
2070 		node = summary_find_next_flagged_msg
2071 			(summaryview, node, MSG_NEW, TRUE);
2072 	else
2073 		node = summary_find_prev_flagged_msg
2074 			(summaryview, node, MSG_NEW, TRUE);
2075 
2076 	if (node)
2077 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2078 	else {
2079 		AlertValue val = 0;
2080 
2081  		switch (prefs_common.next_unread_msg_dialog) {
2082  			case NEXTUNREADMSGDIALOG_ALWAYS:
2083 				val = alertpanel(_("No more new messages"),
2084 						 _("No new message found. "
2085 						   "Go to next folder?"),
2086 						 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2087  				break;
2088  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
2089  				val = G_ALERTALTERNATE;
2090  				break;
2091  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
2092  				val = G_ALERTOTHER;
2093  				break;
2094  			default:
2095  				debug_print(
2096  					_("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2097  		}
2098 		if (val == G_ALERTALTERNATE)
2099 			folderview_select_next_with_flag(summaryview->folderview, MSG_NEW);
2100 	}
2101 }
2102 
summary_select_prev_marked(SummaryView * summaryview)2103 void summary_select_prev_marked(SummaryView *summaryview)
2104 {
2105 	GtkCMCTreeNode *node;
2106 
2107 	if (summaryview->sort_type == SORT_ASCENDING)
2108 		node = summary_find_prev_flagged_msg
2109 			(summaryview, summaryview->selected, MSG_MARKED, TRUE);
2110 	else
2111 		node = summary_find_next_flagged_msg
2112 			(summaryview, summaryview->selected, MSG_MARKED, TRUE);
2113 
2114 	if (!node) {
2115 		AlertValue val;
2116 
2117 		val = alertpanel(_("No more marked messages"),
2118 				 _("No marked message found. "
2119 				   "Search from the end?"),
2120 				 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2121 		if (val != G_ALERTALTERNATE) return;
2122 		node = summary_find_prev_flagged_msg(summaryview, NULL,
2123 						     MSG_MARKED, TRUE);
2124 	}
2125 
2126 	if (!node)
2127 		alertpanel_notice(_("No marked messages."));
2128 	else
2129 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2130 }
2131 
summary_select_next_marked(SummaryView * summaryview)2132 void summary_select_next_marked(SummaryView *summaryview)
2133 {
2134 	GtkCMCTreeNode *node = summaryview->selected;
2135 
2136 	if (summaryview->sort_type == SORT_ASCENDING)
2137 		node = summary_find_next_flagged_msg
2138 			(summaryview, node, MSG_MARKED, TRUE);
2139 	else
2140 		node = summary_find_prev_flagged_msg
2141 			(summaryview, node, MSG_MARKED, TRUE);
2142 
2143 	if (node)
2144 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2145 	else {
2146 		AlertValue val = 0;
2147 
2148  		switch (prefs_common.next_unread_msg_dialog) {
2149  			case NEXTUNREADMSGDIALOG_ALWAYS:
2150 				val = alertpanel(_("No more marked messages"),
2151 						 _("No marked message found. "
2152 						   "Go to next folder?"),
2153 						 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2154  				break;
2155  			case NEXTUNREADMSGDIALOG_ASSUME_YES:
2156  				val = G_ALERTALTERNATE;
2157  				break;
2158  			case NEXTUNREADMSGDIALOG_ASSUME_NO:
2159  				val = G_ALERTOTHER;
2160  				break;
2161  			default:
2162  				debug_print(
2163  					_("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2164  		}
2165 		if (val == G_ALERTALTERNATE)
2166 			folderview_select_next_with_flag(summaryview->folderview, MSG_MARKED);
2167 	}
2168 }
2169 
summary_select_prev_labeled(SummaryView * summaryview)2170 void summary_select_prev_labeled(SummaryView *summaryview)
2171 {
2172 	GtkCMCTreeNode *node;
2173 
2174 	if (summaryview->sort_type == SORT_ASCENDING)
2175 		node = summary_find_prev_flagged_msg
2176 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2177 	else
2178 		node = summary_find_next_flagged_msg
2179 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2180 
2181 	if (!node) {
2182 		AlertValue val;
2183 
2184 		val = alertpanel(_("No more labeled messages"),
2185 				 _("No labeled message found. "
2186 				   "Search from the end?"),
2187 				 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2188 		if (val != G_ALERTALTERNATE) return;
2189 		node = summary_find_prev_flagged_msg(summaryview, NULL,
2190 						     MSG_CLABEL_FLAG_MASK, TRUE);
2191 	}
2192 
2193 	if (!node)
2194 		alertpanel_notice(_("No labeled messages."));
2195 	else
2196 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2197 }
2198 
summary_select_next_labeled(SummaryView * summaryview)2199 void summary_select_next_labeled(SummaryView *summaryview)
2200 {
2201 	GtkCMCTreeNode *node;
2202 
2203 	if (summaryview->sort_type == SORT_ASCENDING)
2204 		node = summary_find_next_flagged_msg
2205 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2206 	else
2207 		node = summary_find_prev_flagged_msg
2208 			(summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2209 
2210 	if (!node) {
2211 		AlertValue val;
2212 
2213 		val = alertpanel(_("No more labeled messages"),
2214 				 _("No labeled message found. "
2215 				   "Search from the beginning?"),
2216 				 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_SECOND);
2217 		if (val != G_ALERTALTERNATE) return;
2218 		if (summaryview->sort_type == SORT_ASCENDING)
2219 			node = summary_find_next_flagged_msg(summaryview, NULL,
2220 						    	     MSG_CLABEL_FLAG_MASK, TRUE);
2221 		else
2222 			node = summary_find_prev_flagged_msg(summaryview, NULL,
2223 						    	     MSG_CLABEL_FLAG_MASK, TRUE);
2224 	}
2225 
2226 	if (!node)
2227 		alertpanel_notice(_("No labeled messages."));
2228 	else
2229 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2230 }
2231 
summary_select_parent(SummaryView * summaryview)2232 void summary_select_parent(SummaryView *summaryview)
2233 {
2234 	GtkCMCTreeNode *node = NULL;
2235 
2236 	if (summaryview->selected)
2237 		node = GTK_CMCTREE_ROW(summaryview->selected)->parent;
2238 	if (node)
2239 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2240 }
2241 
summary_select_by_msgnum(SummaryView * summaryview,guint msgnum,gboolean show)2242 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum,
2243 		gboolean show)
2244 {
2245 	GtkCMCTreeNode *node;
2246 
2247 	node = summary_find_msg_by_msgnum(summaryview, msgnum);
2248 	summary_select_node(summaryview, node, show);
2249 }
2250 
summary_select_by_msg_list(SummaryView * summaryview,GSList * msginfos)2251 void summary_select_by_msg_list(SummaryView	*summaryview, GSList *msginfos)
2252 {
2253 	GtkCMCTree *ctree;
2254 	GSList *msgnum_list, *walk;
2255 	gboolean froze = FALSE;
2256 
2257 	ctree = GTK_CMCTREE(summaryview->ctree);
2258 
2259 	msgnum_list = procmsg_get_number_list_for_msgs(msginfos);
2260 
2261 	START_LONG_OPERATION(summaryview, FALSE);
2262 	for(walk = msgnum_list; walk; walk = walk->next) {
2263 		GtkCMCTreeNode *node;
2264 		node = summary_find_msg_by_msgnum(summaryview, GPOINTER_TO_UINT(walk->data));
2265 		if (node != NULL)
2266 			gtk_cmctree_select(ctree, node);
2267 	}
2268 	END_LONG_OPERATION(summaryview);
2269 	g_slist_free(msgnum_list);
2270 }
2271 
2272 typedef struct _PostponedSelectData
2273 {
2274 	GtkCMCTree *ctree;
2275 	GtkCMCTreeNode *row;
2276 	GtkCMCTreeNode *node;
2277 	GtkScrollType type;
2278 	gint column;
2279 	SummaryView *summaryview;
2280 	gboolean display_msg;
2281 } PostponedSelectData;
2282 
summary_select_retry(void * data)2283 static gboolean summary_select_retry(void *data)
2284 {
2285 	PostponedSelectData *psdata = (PostponedSelectData *)data;
2286 	debug_print("trying again\n");
2287 	if (psdata->row)
2288 		summary_selected(psdata->ctree, psdata->row,
2289 			    psdata->column, psdata->summaryview);
2290 	else if (psdata->node)
2291 		summary_select_node(psdata->summaryview, psdata->node,
2292 			    psdata->display_msg);
2293 	g_free(psdata);
2294 	return FALSE;
2295 }
2296 
2297 /**
2298  * summary_select_node:
2299  * @summaryview: Summary view.
2300  * @node: Summary tree node.
2301  * @display_msg: whether to also display the message
2302  *
2303  * Select @node (bringing it into view by scrolling and expanding its
2304  * thread, if necessary) and unselect all others.
2305  **/
2306 
summary_select_node(SummaryView * summaryview,GtkCMCTreeNode * node,gboolean display_msg)2307 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node,
2308 			 gboolean display_msg)
2309 {
2310 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2311 
2312 	/* If msgview is hidden, we never want to automatically display
2313 	 * a selected message, since that would unhide the msgview. */
2314 	if (!messageview_is_visible(summaryview->messageview))
2315 		display_msg = FALSE;
2316 
2317 	if (summary_is_locked(summaryview)
2318 	&& !GTK_SCTREE(ctree)->selecting_range
2319 	&& summaryview->messageview->mimeview
2320 	&& summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
2321 	&& summaryview->messageview->mimeview->textview->loading) {
2322 		PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2323 		summaryview->messageview->mimeview->textview->stop_loading = TRUE;
2324 
2325 		data->ctree = ctree;
2326 		data->row = NULL;
2327 		data->node = node;
2328 		data->summaryview = summaryview;
2329 		data->display_msg = display_msg;
2330 		debug_print("postponing open of message till end of load\n");
2331 		g_timeout_add(100, summary_select_retry, data);
2332 		return;
2333 	}
2334 	if (summary_is_locked(summaryview)) {
2335 		return;
2336 	}
2337 	if (!summaryview->folder_item)
2338 		return;
2339 	if (node) {
2340 		gtkut_ctree_expand_parent_all(ctree, node);
2341 
2342 		summary_lock(summaryview);
2343 		GTK_EVENTS_FLUSH();
2344 		summary_unlock(summaryview);
2345 
2346 		/* If quicksearch has focus, let's keep it there. */
2347 		if (!quicksearch_has_focus(summaryview->quicksearch) ||
2348 		    quicksearch_is_running(summaryview->quicksearch))
2349 			summary_grab_focus(summaryview);
2350 
2351 		gtkut_ctree_node_move_if_on_the_edge(ctree, node, -1);
2352 
2353 		if (display_msg && summaryview->displayed == node)
2354 			summaryview->displayed = NULL;
2355 		summaryview->display_msg = display_msg;
2356 		gtk_sctree_select(GTK_SCTREE(ctree), node);
2357 		if (summaryview->selected == NULL)
2358 			summaryview->selected = node;
2359 	}
2360 }
2361 
summary_get_msgnum(SummaryView * summaryview,GtkCMCTreeNode * node)2362 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node)
2363 {
2364 	GtkCMCTree *ctree =NULL;
2365 	MsgInfo *msginfo;
2366 
2367 	if (!summaryview)
2368 		return 0;
2369 	ctree = GTK_CMCTREE(summaryview->ctree);
2370 	if (!node)
2371 		return 0;
2372 	msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2373 	if (msginfo)
2374 		return msginfo->msgnum;
2375 	else
2376 		return -1;
2377 }
2378 
summary_find_prev_msg(SummaryView * summaryview,GtkCMCTreeNode * current_node,gboolean start_from_prev)2379 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
2380 					     GtkCMCTreeNode *current_node,
2381 					     gboolean start_from_prev)
2382 {
2383 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2384 	GtkCMCTreeNode *node;
2385 	MsgInfo *msginfo;
2386 
2387 	if (current_node) {
2388 		if (start_from_prev)
2389 			node = gtkut_ctree_node_prev(ctree, current_node);
2390 		else
2391 			node = current_node;
2392 	} else
2393 		node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2394 
2395 	for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2396 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2397 		if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
2398 	}
2399 
2400 	return node;
2401 }
2402 
summary_find_next_msg(SummaryView * summaryview,GtkCMCTreeNode * current_node,gboolean start_from_next)2403 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview,
2404 					    GtkCMCTreeNode *current_node,
2405 					    gboolean start_from_next)
2406 {
2407 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2408 	GtkCMCTreeNode *node;
2409 	MsgInfo *msginfo;
2410 
2411 	if (current_node) {
2412 		if (start_from_next)
2413 			node = gtkut_ctree_node_next(ctree, current_node);
2414 		else
2415 			node = current_node;
2416 	} else
2417 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2418 
2419 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2420 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2421 		if (msginfo && !MSG_IS_DELETED(msginfo->flags)
2422 		&& !MSG_IS_MOVE(msginfo->flags)) break;
2423 	}
2424 
2425 	return node;
2426 }
2427 
summary_find_prev_flagged_msg(SummaryView * summaryview,GtkCMCTreeNode * current_node,MsgPermFlags flags,gboolean start_from_prev)2428 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
2429 						   GtkCMCTreeNode *current_node,
2430 						   MsgPermFlags flags,
2431 						   gboolean start_from_prev)
2432 {
2433 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2434 	GtkCMCTreeNode *node;
2435 	MsgInfo *msginfo;
2436 
2437 	if (current_node) {
2438 		if (start_from_prev)
2439 			node = gtkut_ctree_node_prev(ctree, current_node);
2440 		else
2441 			node = current_node;
2442 	} else
2443 		node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2444 
2445 	for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2446 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2447 		if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
2448 	}
2449 
2450 	return node;
2451 }
2452 
summary_find_next_flagged_msg(SummaryView * summaryview,GtkCMCTreeNode * current_node,MsgPermFlags flags,gboolean start_from_next)2453 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
2454 						   GtkCMCTreeNode *current_node,
2455 						   MsgPermFlags flags,
2456 						   gboolean start_from_next)
2457 {
2458 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2459 	GtkCMCTreeNode *node;
2460 	MsgInfo *msginfo;
2461 
2462 	if (current_node) {
2463 		if (start_from_next)
2464 			node = gtkut_ctree_node_next(ctree, current_node);
2465 		else
2466 			node = current_node;
2467 	} else
2468 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2469 
2470 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2471 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2472 		/* Find msg with matching flags but ignore messages with
2473 		   ignore flags, if searching for new or unread messages */
2474 		if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
2475 		    !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags))
2476 			)
2477 			break;
2478 	}
2479 
2480 	return node;
2481 }
2482 
summary_find_msg_by_msgnum(SummaryView * summaryview,guint msgnum)2483 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
2484 						guint msgnum)
2485 {
2486 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2487 	GtkCMCTreeNode *node;
2488 	MsgInfo *msginfo;
2489 
2490 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2491 
2492 	for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2493 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2494 		if (msginfo && msginfo->msgnum == msgnum) break;
2495 	}
2496 
2497 	return node;
2498 }
2499 
attract_hash_func(gconstpointer key)2500 static guint attract_hash_func(gconstpointer key)
2501 {
2502 	gchar *str;
2503 	gchar *p;
2504 	guint h;
2505 
2506 	Xstrdup_a(str, (const gchar *)key, return 0);
2507 	trim_subject(str);
2508 
2509 	p = str;
2510 	h = *p;
2511 
2512 	if (h) {
2513 		for (p += 1; *p != '\0'; p++)
2514 			h = (h << 5) - h + *p;
2515 	}
2516 
2517 	return h;
2518 }
2519 
attract_compare_func(gconstpointer a,gconstpointer b)2520 static gint attract_compare_func(gconstpointer a, gconstpointer b)
2521 {
2522 	return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2523 }
2524 
summary_attract_by_subject(SummaryView * summaryview)2525 void summary_attract_by_subject(SummaryView *summaryview)
2526 {
2527 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2528 	GtkCMCList *clist = GTK_CMCLIST(ctree);
2529 	GtkCMCTreeNode *src_node;
2530 	GtkCMCTreeNode *dst_node, *sibling;
2531 	GtkCMCTreeNode *tmp;
2532 	MsgInfo *src_msginfo, *dst_msginfo;
2533 	GHashTable *subject_table;
2534 
2535 	debug_print("Attracting messages by subject...\n");
2536 	STATUSBAR_PUSH(summaryview->mainwin,
2537 		       _("Attracting messages by subject..."));
2538 
2539 	main_window_cursor_wait(summaryview->mainwin);
2540 	summary_freeze(summaryview);
2541 
2542 	subject_table = g_hash_table_new(attract_hash_func,
2543 					 attract_compare_func);
2544 
2545 	for (src_node = GTK_CMCTREE_NODE(clist->row_list);
2546 	     src_node != NULL;
2547 	     src_node = tmp) {
2548 		tmp = GTK_CMCTREE_ROW(src_node)->sibling;
2549 		src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
2550 		if (!src_msginfo) continue;
2551 		if (!src_msginfo->subject) continue;
2552 
2553 		/* find attracting node */
2554 		dst_node = g_hash_table_lookup(subject_table,
2555 					       src_msginfo->subject);
2556 
2557 		if (dst_node) {
2558 			dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
2559 
2560 			/* if the time difference is more than 20 days,
2561 			   don't attract */
2562 			if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
2563 			    > 60 * 60 * 24 * 20)
2564 				continue;
2565 
2566 			sibling = GTK_CMCTREE_ROW(dst_node)->sibling;
2567 			if (src_node != sibling)
2568 				gtk_cmctree_move(ctree, src_node, NULL, sibling);
2569 		}
2570 
2571 		g_hash_table_insert(subject_table,
2572 				    src_msginfo->subject, src_node);
2573 	}
2574 
2575 	g_hash_table_destroy(subject_table);
2576 
2577 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
2578 
2579 	summary_thaw(summaryview);
2580 
2581 	debug_print("Attracting messages by subject done.\n");
2582 	STATUSBAR_POP(summaryview->mainwin);
2583 
2584 	main_window_cursor_normal(summaryview->mainwin);
2585 }
2586 
summary_free_msginfo_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2587 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2588 				      gpointer data)
2589 {
2590 	MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2591 
2592 	if (msginfo)
2593 		procmsg_msginfo_free(&msginfo);
2594 }
2595 
summary_set_marks_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2596 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2597 				   gpointer data)
2598 {
2599 	SummaryView *summaryview = data;
2600 	MsgInfo *msginfo;
2601 
2602 	msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2603 
2604 	cm_return_if_fail(msginfo != NULL);
2605 
2606 	if (MSG_IS_DELETED(msginfo->flags))
2607 		summaryview->deleted++;
2608 
2609 	summaryview->total_size += msginfo->size;
2610 
2611 	summary_set_row_marks(summaryview, node);
2612 }
2613 
summary_update_status(SummaryView * summaryview)2614 static void summary_update_status(SummaryView *summaryview)
2615 {
2616 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2617 	GtkCMCTreeNode *node;
2618 	MsgInfo *msginfo;
2619 
2620 	summaryview->total_size =
2621 	summaryview->deleted = summaryview->moved = summaryview->copied = 0;
2622 
2623 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2624 	     node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2625 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2626 
2627 		if (!msginfo)
2628 			continue;
2629 
2630 		if (MSG_IS_DELETED(msginfo->flags))
2631 			summaryview->deleted++;
2632 		if (MSG_IS_MOVE(msginfo->flags))
2633 			summaryview->moved++;
2634 		if (MSG_IS_COPY(msginfo->flags))
2635 			summaryview->copied++;
2636 		summaryview->total_size += msginfo->size;
2637 	}
2638 }
2639 
summary_status_show(SummaryView * summaryview)2640 static void summary_status_show(SummaryView *summaryview)
2641 {
2642 	gchar *str;
2643 	gchar *del, *mv, *cp;
2644 	gchar *sel;
2645 	gchar *spc;
2646 	gchar *itstr;
2647 	GList *rowlist, *cur;
2648 	guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
2649 	guint n_marked = 0, n_replied = 0, n_forwarded = 0, n_locked = 0, n_ignored = 0, n_watched = 0;
2650 	goffset sel_size = 0, n_size = 0;
2651 	MsgInfo *msginfo;
2652 	gchar *name;
2653 	gchar *tooltip;
2654 
2655 	if (!summaryview->folder_item) {
2656 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
2657 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
2658 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs),   "");
2659 		toolbar_main_set_sensitive(summaryview->mainwin);
2660 		return;
2661 	}
2662 
2663 	rowlist = GTK_CMCLIST(summaryview->ctree)->selection;
2664 	for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2665 		msginfo = gtk_cmctree_node_get_row_data
2666 			(GTK_CMCTREE(summaryview->ctree),
2667 			 GTK_CMCTREE_NODE(cur->data));
2668 		if (msginfo) {
2669 			sel_size += msginfo->size;
2670 			n_selected++;
2671 		}
2672 	}
2673 
2674 	if (summaryview->folder_item->hide_read_msgs
2675 	|| summaryview->folder_item->hide_del_msgs
2676 	|| summaryview->folder_item->hide_read_threads
2677 	|| quicksearch_has_sat_predicate(summaryview->quicksearch)) {
2678 		rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
2679 		for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2680 			msginfo = gtk_cmctree_node_get_row_data
2681 				(GTK_CMCTREE(summaryview->ctree),
2682 				 GTK_CMCTREE_NODE(cur));
2683 			if (msginfo) {
2684 				n_size += msginfo->size;
2685 				n_total++;
2686 				if (MSG_IS_NEW(msginfo->flags))
2687 					n_new++;
2688 				if (MSG_IS_UNREAD(msginfo->flags))
2689 					n_unread++;
2690 				if (MSG_IS_MARKED(msginfo->flags))
2691 					n_marked++;
2692 				if (MSG_IS_REPLIED(msginfo->flags))
2693 					n_replied++;
2694 				if (MSG_IS_FORWARDED(msginfo->flags))
2695 					n_forwarded++;
2696 				if (MSG_IS_LOCKED(msginfo->flags))
2697 					n_locked++;
2698 				if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2699 					n_ignored++;
2700 				if (MSG_IS_WATCH_THREAD(msginfo->flags))
2701 					n_watched++;
2702 			}
2703 		}
2704 	} else {
2705 		n_new = summaryview->folder_item->new_msgs;
2706 		n_unread = summaryview->folder_item->unread_msgs;
2707 		n_marked = summaryview->folder_item->marked_msgs;
2708 		n_replied = summaryview->folder_item->replied_msgs;
2709 		n_forwarded = summaryview->folder_item->forwarded_msgs;
2710 		n_locked = summaryview->folder_item->locked_msgs;
2711 		n_ignored = summaryview->folder_item->ignored_msgs;
2712 		n_watched = summaryview->folder_item->watched_msgs;
2713 		n_total = summaryview->folder_item->total_msgs;
2714 		n_size = summaryview->total_size;
2715 	}
2716 
2717 	name = folder_item_get_name(summaryview->folder_item);
2718 	gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
2719 	g_free(name);
2720 
2721 	if (summaryview->deleted)
2722 		del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
2723 	else
2724 		del = g_strdup("");
2725 	if (summaryview->moved)
2726 		mv = g_strdup_printf(_("%s%d moved"),
2727 				     summaryview->deleted ? _(", ") : "",
2728 				     summaryview->moved);
2729 	else
2730 		mv = g_strdup("");
2731 	if (summaryview->copied)
2732 		cp = g_strdup_printf(_("%s%d copied"),
2733 				     summaryview->deleted ||
2734 				     summaryview->moved ? _(", ") : "",
2735 				     summaryview->copied);
2736 	else
2737 		cp = g_strdup("");
2738 
2739 	if (summaryview->deleted || summaryview->moved || summaryview->copied)
2740 		spc = "    ";
2741 	else
2742 		spc = "";
2743 
2744 	if (n_selected) {
2745 		sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size));
2746 		itstr = g_strdup_printf(ngettext(" item selected"," items selected", n_selected));
2747 	} else {
2748 		sel = g_strdup("");
2749 		itstr = g_strdup("");
2750 	}
2751 
2752 	if (prefs_common.layout_mode != SMALL_LAYOUT) {
2753 		str = g_strconcat(n_selected ? itos(n_selected) : "",
2754 						itstr, sel, spc, del, mv, cp, NULL);
2755 		g_free(sel);
2756 		g_free(del);
2757 		g_free(mv);
2758 		g_free(cp);
2759 		g_free(itstr);
2760 
2761 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2762 		g_free(str);
2763 
2764 		str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2765 					      n_new, n_unread, n_total,
2766 					      to_human_readable((goffset)n_size));
2767 
2768 	g_signal_connect(G_OBJECT(summaryview->popupmenu), "selection-done",
2769 			G_CALLBACK(popup_menu_selection_done), summaryview);
2770 
2771 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
2772 		g_free(str);
2773 		tooltip = g_strdup_printf("<b>%s</b>\n"
2774 					"<b>%s</b> %d\n"
2775 					"<b>%s</b> %d\n"
2776 					"<b>%s</b> %d\n"
2777 					"<b>%s</b> %s\n\n"
2778 					"<b>%s</b> %d\n"
2779 					"<b>%s</b> %d\n"
2780 					"<b>%s</b> %d\n"
2781 					"<b>%s</b> %d\n"
2782 					"<b>%s</b> %d\n"
2783 					"<b>%s</b> %d",
2784 					_("Message summary"),
2785 					_("New:"), n_new,
2786 					_("Unread:"), n_unread,
2787 					_("Total:"), n_total,
2788 					_("Size:"), to_human_readable((goffset)n_size),
2789 					_("Marked:"), n_marked,
2790 					_("Replied:"), n_replied,
2791 					_("Forwarded:"), n_forwarded,
2792 					_("Locked:"), n_locked,
2793 					_("Ignored:"), n_ignored,
2794 					_("Watched:"), n_watched);
2795 
2796 		gtk_widget_set_tooltip_markup(GTK_WIDGET(summaryview->statlabel_msgs),
2797 				            tooltip);
2798 		g_free(tooltip);
2799 	} else {
2800 		gchar *ssize, *tsize;
2801 		if (n_selected) {
2802 			ssize = g_strdup(to_human_readable((goffset)sel_size));
2803 			tsize = g_strdup(to_human_readable((goffset)n_size));
2804 			str = g_strdup_printf(_("%d/%d selected (%s/%s), %d unread"),
2805 				n_selected, n_total, ssize, tsize, n_unread);
2806 			g_free(ssize);
2807 			g_free(tsize);
2808 		} else
2809 			str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2810 				n_new, n_unread, n_total, to_human_readable((goffset)n_size));
2811 		g_free(sel);
2812 		g_free(del);
2813 		g_free(mv);
2814 		g_free(cp);
2815 		g_free(itstr);
2816 
2817 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2818 		g_free(str);
2819 	}
2820 
2821 	summary_set_menu_sensitive(summaryview);
2822 	toolbar_main_set_sensitive(summaryview->mainwin);
2823 }
2824 
summary_set_column_titles(SummaryView * summaryview)2825 static void summary_set_column_titles(SummaryView *summaryview)
2826 {
2827 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2828 	FolderItem *item = summaryview->folder_item;
2829 	GtkWidget *hbox;
2830 	GtkWidget *label;
2831 	GtkWidget *arrow;
2832 	gint pos;
2833 	const gchar *title;
2834 	SummaryColumnType type;
2835 	GtkJustification justify;
2836 
2837 	static FolderSortKey sort_by[N_SUMMARY_COLS] = {
2838 		SORT_BY_MARK,
2839 		SORT_BY_STATUS,
2840 		SORT_BY_MIME,
2841 		SORT_BY_SUBJECT,
2842 		SORT_BY_FROM,
2843 		SORT_BY_TO,
2844 		SORT_BY_DATE,
2845 		SORT_BY_SIZE,
2846 		SORT_BY_NUMBER,
2847 		SORT_BY_SCORE,
2848 		SORT_BY_LOCKED,
2849 		SORT_BY_TAGS
2850 	};
2851 
2852 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
2853 		type = summaryview->col_state[pos].type;
2854 
2855 		/* CLAWS: mime and unread are single char headers */
2856 		justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
2857 			? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
2858 
2859 		switch (type) {
2860 		case S_COL_SUBJECT:
2861 		case S_COL_FROM:
2862 		case S_COL_TO:
2863 		case S_COL_DATE:
2864 		case S_COL_NUMBER:
2865 			if(type == S_COL_FROM && item != NULL &&
2866 					FOLDER_SHOWS_TO_HDR(item) &&
2867 					!summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
2868 				type = S_COL_TO;
2869 			if(type == S_COL_NUMBER)
2870 				title = gettext(col_label[type]);
2871 			else
2872 				title = prefs_common_translated_header_name(col_label[type]);
2873 			break;
2874 		default:
2875 			title = gettext(col_label[type]);
2876 		}
2877 
2878 		if (type == S_COL_MIME) {
2879 			label = gtk_image_new_from_pixbuf(clipxpm);
2880 			gtk_widget_show(label);
2881 			gtk_cmclist_set_column_widget(clist, pos, label);
2882 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment"));
2883 			continue;
2884 		} else if (type == S_COL_MARK) {
2885 			label = gtk_image_new_from_pixbuf(markxpm);
2886 			gtk_widget_show(label);
2887 			gtk_cmclist_set_column_widget(clist, pos, label);
2888 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Mark"));
2889 			continue;
2890 		} else if (type == S_COL_LOCKED) {
2891 			label = gtk_image_new_from_pixbuf(lockedxpm);
2892 			gtk_widget_show(label);
2893 			gtk_cmclist_set_column_widget(clist, pos, label);
2894 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked"));
2895 			continue;
2896 		} else if (type == S_COL_STATUS) {
2897 			gtk_cmclist_set_column_title(clist, pos, title);
2898 			gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status"));
2899 			continue;
2900 		}
2901 
2902 		hbox  = gtk_hbox_new(FALSE, 4);
2903 		label = gtk_label_new(title);
2904 #ifdef GENERIC_UMPC
2905 	gtk_widget_set_size_request(hbox, -1, 20);
2906 #endif
2907 
2908 		if (justify == GTK_JUSTIFY_RIGHT)
2909 			gtk_box_pack_end(GTK_BOX(hbox), label,
2910 					 FALSE, FALSE, 0);
2911 		else
2912 			gtk_box_pack_start(GTK_BOX(hbox), label,
2913 					   FALSE, FALSE, 0);
2914 
2915 		if (summaryview->sort_key == sort_by[type] ||
2916 		    (summaryview->sort_key == SORT_BY_THREAD_DATE &&
2917 		     sort_by[SORT_BY_DATE] && type == S_COL_DATE)) {
2918 			arrow = gtk_arrow_new
2919 				(summaryview->sort_type == SORT_ASCENDING
2920 				 ? GTK_ARROW_DOWN : GTK_ARROW_UP,
2921 				 GTK_SHADOW_IN);
2922 			gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10);
2923 			if (justify == GTK_JUSTIFY_RIGHT)
2924 				gtk_box_pack_start(GTK_BOX(hbox), arrow,
2925 						   FALSE, FALSE, 0);
2926 			else
2927 				gtk_box_pack_end(GTK_BOX(hbox), arrow,
2928 						 FALSE, FALSE, 0);
2929 		}
2930 
2931 		gtk_widget_show_all(hbox);
2932 		gtk_cmclist_set_column_widget(clist, pos, hbox);
2933 	}
2934 }
2935 
summary_reflect_tags_changes(SummaryView * summaryview)2936 void summary_reflect_tags_changes(SummaryView *summaryview)
2937 {
2938 	GtkMenuShell *menu;
2939 	GList *children, *cur;
2940 	GtkCMCTreeNode *node;
2941 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2942 	gboolean froze = FALSE;
2943 	gboolean redisplay = FALSE;
2944 
2945 	/* re-create colorlabel submenu */
2946 	menu = GTK_MENU_SHELL(summaryview->tags_menu);
2947 	cm_return_if_fail(menu != NULL);
2948 
2949 	/* clear items. get item pointers. */
2950 	children = gtk_container_get_children(GTK_CONTAINER(menu));
2951 	for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
2952 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
2953 	}
2954 	g_list_free(children);
2955 	summary_tags_menu_create(summaryview, TRUE);
2956 
2957 	START_LONG_OPERATION(summaryview, TRUE);
2958 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
2959 	     node = gtkut_ctree_node_next(ctree, node)) {
2960 		redisplay |= summary_set_row_tag(summaryview,
2961 					   node, TRUE, FALSE, 0);
2962 	}
2963 	END_LONG_OPERATION(summaryview);
2964 	if (redisplay)
2965 		summary_redisplay_msg(summaryview);
2966 }
2967 
2968 
summary_reflect_prefs(void)2969 void summary_reflect_prefs(void)
2970 {
2971 	static gchar *last_smallfont = NULL;
2972 	static gchar *last_normalfont = NULL;
2973 	static gchar *last_boldfont = NULL;
2974 	static gboolean last_derive = 0;
2975 	gboolean update_font = FALSE;
2976 	SummaryView *summaryview = NULL;
2977 
2978 	if (!mainwindow_get_mainwindow())
2979 		return;
2980 	summaryview = mainwindow_get_mainwindow()->summaryview;
2981 
2982 	if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2983 			!last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2984 			!last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2985 			last_derive != prefs_common.derive_from_normal_font)
2986 		update_font = TRUE;
2987 
2988 	g_free(last_smallfont);
2989 	last_smallfont = g_strdup(SMALL_FONT);
2990 	g_free(last_normalfont);
2991 	last_normalfont = g_strdup(NORMAL_FONT);
2992 	g_free(last_boldfont);
2993 	last_boldfont = g_strdup(BOLD_FONT);
2994 	last_derive = prefs_common.derive_from_normal_font;
2995 
2996 #define STYLE_FREE(s)			\
2997 	if (s != NULL) {		\
2998 		g_object_unref(s);	\
2999 		s = NULL;		\
3000 	}
3001 
3002 	if (update_font) {
3003 		STYLE_FREE(bold_style);
3004 		STYLE_FREE(bold_style);
3005 		STYLE_FREE(bold_marked_style);
3006 		STYLE_FREE(bold_deleted_style);
3007 		STYLE_FREE(small_style);
3008 		STYLE_FREE(small_marked_style);
3009 		STYLE_FREE(small_deleted_style);
3010 		summary_set_fonts(summaryview);
3011 	}
3012 
3013 #undef STYLE_FREE
3014 
3015 	summary_set_column_titles(summaryview);
3016 	summary_relayout(summaryview);
3017 
3018 	if (summaryview->folder_item)
3019 		summary_show(summaryview, summaryview->folder_item, FALSE);
3020 }
3021 
summary_sort(SummaryView * summaryview,FolderSortKey sort_key,FolderSortType sort_type)3022 void summary_sort(SummaryView *summaryview,
3023 		  FolderSortKey sort_key, FolderSortType sort_type)
3024 {
3025 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3026 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
3027 	GtkCMCListCompareFunc cmp_func = NULL;
3028 	START_TIMING("");
3029 	g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
3030 				       G_CALLBACK(summary_tree_expanded), summaryview);
3031 	summary_freeze(summaryview);
3032 
3033 	switch (sort_key) {
3034 	case SORT_BY_MARK:
3035 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark;
3036 		break;
3037 	case SORT_BY_STATUS:
3038 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status;
3039 		break;
3040 	case SORT_BY_MIME:
3041 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime;
3042 		break;
3043 	case SORT_BY_NUMBER:
3044 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num;
3045 		break;
3046 	case SORT_BY_SIZE:
3047 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size;
3048 		break;
3049 	case SORT_BY_DATE:
3050 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date;
3051 		break;
3052 	case SORT_BY_THREAD_DATE:
3053 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date;
3054 		break;
3055 	case SORT_BY_FROM:
3056 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from;
3057 		break;
3058 	case SORT_BY_SUBJECT:
3059 		if (summaryview->simplify_subject_preg)
3060 			cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject;
3061 		else
3062 			cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject;
3063 		break;
3064 	case SORT_BY_SCORE:
3065 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score;
3066 		break;
3067 	case SORT_BY_LABEL:
3068 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label;
3069 		break;
3070 	case SORT_BY_TO:
3071 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to;
3072 		break;
3073 	case SORT_BY_LOCKED:
3074 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked;
3075 		break;
3076 	case SORT_BY_TAGS:
3077 		cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_tags;
3078 		break;
3079 	case SORT_BY_NONE:
3080 		break;
3081 	default:
3082 		goto unlock;
3083 	}
3084 
3085 	summaryview->sort_key = sort_key;
3086 	summaryview->sort_type = sort_type;
3087 
3088 	summary_set_column_titles(summaryview);
3089 	summary_set_menu_sensitive(summaryview);
3090 
3091 	/* allow fallback to don't sort */
3092 	if (summaryview->sort_key == SORT_BY_NONE)
3093 		goto unlock;
3094 
3095 	if (cmp_func != NULL) {
3096 		debug_print("Sorting summary...\n");
3097 		STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
3098 
3099 		main_window_cursor_wait(summaryview->mainwin);
3100 
3101 		gtk_cmclist_set_compare_func(clist, cmp_func);
3102 
3103 		gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type);
3104 		gtk_sctree_sort_recursive(ctree, NULL);
3105 
3106 		gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
3107 
3108 		main_window_cursor_normal(summaryview->mainwin);
3109 
3110 		debug_print("Sorting summary done.\n");
3111 		STATUSBAR_POP(summaryview->mainwin);
3112 	}
3113 unlock:
3114 	summary_thaw(summaryview);
3115 	g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
3116 				       G_CALLBACK(summary_tree_expanded), summaryview);
3117 	END_TIMING();
3118 }
3119 
summary_update_thread_age(GNode * node,gpointer data)3120 static gboolean summary_update_thread_age(GNode *node, gpointer data)
3121 {
3122 	MsgInfo *msginfo = node->data;
3123 	time_t *most_recent = (time_t *)data;
3124 
3125 	if (msginfo->date_t > *most_recent) {
3126 		*most_recent = msginfo->date_t;
3127 	}
3128 	return FALSE;
3129 }
3130 
summary_find_thread_age(GNode * gnode)3131 static void summary_find_thread_age(GNode *gnode)
3132 {
3133 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
3134 	time_t most_recent;
3135 
3136 	if (!msginfo)
3137 		return;
3138 	most_recent = msginfo->thread_date = msginfo->date_t;
3139 
3140 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent);
3141 
3142 	msginfo->thread_date = most_recent;
3143 }
3144 
summary_update_is_read(GNode * node,gpointer data)3145 static gboolean summary_update_is_read(GNode *node, gpointer data)
3146 {
3147 	MsgInfo *msginfo = node->data;
3148 	gboolean *all_read = (gboolean *)data;
3149 
3150 	if (MSG_IS_UNREAD(msginfo->flags)) {
3151 		*all_read = FALSE;
3152 		return TRUE;
3153 	}
3154 	return FALSE;
3155 }
3156 
summary_thread_is_read(GNode * gnode)3157 static gboolean summary_thread_is_read(GNode *gnode)
3158 {
3159 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
3160 	gboolean all_read = TRUE;
3161 
3162 	if (!msginfo)
3163 		return all_read;
3164 
3165 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_is_read, &all_read);
3166     return all_read;
3167 }
3168 
3169 typedef struct  _ThreadSelectedData {
3170 	guint msgnum;
3171 	gboolean is_selected;
3172 } ThreadSelectedData;
3173 
summary_update_is_selected(GNode * gnode,gpointer data)3174 static gboolean summary_update_is_selected(GNode *gnode, gpointer data)
3175 {
3176 	ThreadSelectedData *selected = (ThreadSelectedData *)data;
3177 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
3178 
3179 	if (msginfo->msgnum == selected->msgnum) {
3180 		selected->is_selected = TRUE;
3181 		return TRUE;
3182 	}
3183 
3184 	return FALSE;
3185 }
3186 
summary_thread_is_selected(GNode * gnode,guint selected_msgnum)3187 static gboolean summary_thread_is_selected(GNode *gnode, guint selected_msgnum)
3188 {
3189 	ThreadSelectedData selected;
3190 
3191 	selected.msgnum = selected_msgnum;
3192 	selected.is_selected = FALSE;
3193 	g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1,
3194 			summary_update_is_selected, &selected);
3195 	return selected.is_selected;
3196 }
3197 
summary_insert_gnode_func(GtkCMCTree * ctree,guint depth,GNode * gnode,GtkCMCTreeNode * cnode,gpointer data)3198 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode,
3199 				   GtkCMCTreeNode *cnode, gpointer data)
3200 {
3201 	SummaryView *summaryview = (SummaryView *)data;
3202 	MsgInfo *msginfo = (MsgInfo *)gnode->data;
3203 	gchar *text[N_SUMMARY_COLS];
3204 	gint *col_pos = summaryview->col_pos;
3205 	const gchar *msgid = msginfo->msgid;
3206 	GHashTable *msgid_table = summaryview->msgid_table;
3207 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3208 	gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3209 
3210 	summary_set_header(summaryview, text, msginfo);
3211 
3212 	gtk_cmctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
3213 				NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed);
3214 #define SET_TEXT(col) {						\
3215 	gtk_cmctree_node_set_text(ctree, cnode, col_pos[col], 	\
3216 				text[col_pos[col]]);		\
3217 }
3218 
3219 	if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3220 		SET_TEXT(S_COL_NUMBER);
3221 	if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3222 		SET_TEXT(S_COL_SCORE);
3223 	if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3224 		SET_TEXT(S_COL_SIZE);
3225 	if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible)
3226 		SET_TEXT(S_COL_DATE);
3227 	if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
3228 		SET_TEXT(S_COL_FROM);
3229 	if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
3230 		SET_TEXT(S_COL_TO);
3231 	if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible)
3232 		SET_TEXT(S_COL_TAGS);
3233 
3234 	if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3235 		g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3236 
3237 #undef SET_TEXT
3238 
3239 	GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
3240 	summary_set_marks_func(ctree, cnode, summaryview);
3241 
3242 	if (msgid && msgid[0] != '\0')
3243 		g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
3244 
3245 	return TRUE;
3246 }
3247 
summary_set_ctree_from_list(SummaryView * summaryview,GSList * mlist,guint selected_msgnum)3248 static void summary_set_ctree_from_list(SummaryView *summaryview,
3249 					GSList *mlist, guint selected_msgnum)
3250 {
3251 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3252 	MsgInfo *msginfo;
3253 	GtkCMCTreeNode *node = NULL;
3254 	GHashTable *msgid_table;
3255 	GHashTable *subject_table = NULL;
3256 	GSList * cur;
3257 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3258 	gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3259 	START_TIMING("");
3260 
3261 	if (!mlist) return;
3262 
3263 	debug_print("Setting summary from message data...\n");
3264 	STATUSBAR_PUSH(summaryview->mainwin,
3265 		       _("Setting summary from message data..."));
3266 	gdk_flush();
3267 
3268 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
3269 				       G_CALLBACK(summary_tree_expanded), summaryview);
3270 
3271 	msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
3272 	summaryview->msgid_table = msgid_table;
3273 
3274 	if (prefs_common.thread_by_subject) {
3275 		subject_table = g_hash_table_new(g_str_hash, g_str_equal);
3276 		summaryview->subject_table = subject_table;
3277 	} else {
3278 		summaryview->subject_table = NULL;
3279 	}
3280 
3281 	if (prefs_common.use_addr_book)
3282 		start_address_completion(NULL);
3283 
3284 	if (summaryview->threaded) {
3285 		GNode *root, *gnode;
3286 		START_TIMING("threaded");
3287 		root = procmsg_get_thread_tree(mlist);
3288 
3289 
3290 		for (gnode = root->children; gnode != NULL;
3291 		     gnode = gnode->next) {
3292 			if (!summaryview->folder_item->hide_read_threads ||
3293 			    !summary_thread_is_read(gnode) ||
3294 			    summary_thread_is_selected(gnode, selected_msgnum)) {
3295 				summary_find_thread_age(gnode);
3296 				node = gtk_sctree_insert_gnode
3297 					(ctree, NULL, node, gnode,
3298 					 summary_insert_gnode_func, summaryview);
3299 			}
3300 		}
3301 
3302 		g_node_destroy(root);
3303 
3304 		END_TIMING();
3305 	} else {
3306 		gchar *text[N_SUMMARY_COLS];
3307 		START_TIMING("unthreaded");
3308 		cur = mlist;
3309 		for (; mlist != NULL; mlist = mlist->next) {
3310 			msginfo = (MsgInfo *)mlist->data;
3311 
3312 			summary_set_header(summaryview, text, msginfo);
3313 
3314 			node = gtk_sctree_insert_node
3315 				(ctree, NULL, node, text, 2,
3316 				 NULL, NULL,
3317 				 FALSE, FALSE);
3318 			if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3319 				g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3320 
3321 			GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
3322 			summary_set_marks_func(ctree, node, summaryview);
3323 
3324 			if (msginfo->msgid && msginfo->msgid[0] != '\0')
3325 				g_hash_table_insert(msgid_table,
3326 						    msginfo->msgid, node);
3327 
3328 			if (prefs_common.thread_by_subject)
3329 				subject_table_insert(subject_table,
3330 					     msginfo->subject,
3331 					     node);
3332 		}
3333 		mlist = cur;
3334 		END_TIMING();
3335 	}
3336 
3337 	if (prefs_common.enable_hscrollbar &&
3338 	    summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
3339 		gint optimal_width;
3340 
3341 		optimal_width = gtk_cmclist_optimal_column_width
3342 			(GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
3343 		gtk_cmclist_set_column_width(GTK_CMCLIST(ctree),
3344 					   summaryview->col_pos[S_COL_SUBJECT],
3345 					   optimal_width);
3346 	}
3347 
3348 	if (prefs_common.use_addr_book)
3349 		end_address_completion();
3350 
3351 	debug_print("Setting summary from message data done.\n");
3352 	STATUSBAR_POP(summaryview->mainwin);
3353 	if (debug_get_mode()) {
3354 		debug_print("\tmsgid hash table size = %d\n",
3355 			    g_hash_table_size(msgid_table));
3356 		if (prefs_common.thread_by_subject)
3357 			debug_print("\tsubject hash table size = %d\n",
3358 			    g_hash_table_size(subject_table));
3359 	}
3360 
3361 	summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
3362 
3363 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3364 
3365 	if (prefs_common.bold_unread) {
3366 		START_TIMING("bold_unread");
3367 		while (node) {
3368 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
3369 			if (GTK_CMCTREE_ROW(node)->children)
3370 				summary_set_row_marks(summaryview, node);
3371 			node = next;
3372 		}
3373 		END_TIMING();
3374 	}
3375 
3376 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3377 				       G_CALLBACK(summary_tree_expanded), summaryview);
3378 	END_TIMING();
3379 }
3380 
summary_complete_address(const gchar * addr)3381 static gchar *summary_complete_address(const gchar *addr)
3382 {
3383 	gint count;
3384 	gchar *res, *tmp, *email_addr;
3385 
3386 	if (addr == NULL || !strchr(addr, '@'))
3387 		return NULL;
3388 
3389 	Xstrdup_a(email_addr, addr, return NULL);
3390 	extract_address(email_addr);
3391 	if (!*email_addr)
3392 		return NULL;
3393 
3394 	/*
3395 	 * completion stuff must be already initialized
3396 	 */
3397 	res = NULL;
3398 	if (1 < (count = complete_address(email_addr))) {
3399 		tmp = get_complete_address(1);
3400 		res = procheader_get_fromname(tmp);
3401 		g_free(tmp);
3402 	}
3403 
3404 	return res;
3405 }
3406 
summary_set_header(SummaryView * summaryview,gchar * text[],MsgInfo * msginfo)3407 static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
3408 			       MsgInfo *msginfo)
3409 {
3410 	static gchar date_modified[80];
3411 	static gchar col_score[11];
3412 	static gchar from_buf[BUFFSIZE], to_buf[BUFFSIZE];
3413 	static gchar tmp1[BUFFSIZE], tmp2[BUFFSIZE+4], tmp3[BUFFSIZE];
3414 	gint *col_pos = summaryview->col_pos;
3415 	gchar *from_text = NULL, *to_text = NULL, *tags_text = NULL;
3416 	gboolean should_swap = FALSE;
3417 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3418 	gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3419 	static const gchar *color_dim_rgb = NULL;
3420 	if (!color_dim_rgb)
3421 		color_dim_rgb = gdk_color_to_string(&summaryview->color_dim);
3422 	text[col_pos[S_COL_FROM]]   = "";
3423 	text[col_pos[S_COL_TO]]     = "";
3424 	text[col_pos[S_COL_SUBJECT]]= "";
3425 	text[col_pos[S_COL_MARK]]   = "";
3426 	text[col_pos[S_COL_STATUS]] = "";
3427 	text[col_pos[S_COL_MIME]]   = "";
3428 	text[col_pos[S_COL_LOCKED]] = "";
3429 	text[col_pos[S_COL_DATE]]   = "";
3430 	text[col_pos[S_COL_TAGS]]   = "";
3431 	if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3432 		text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
3433 	else
3434 		text[col_pos[S_COL_NUMBER]] = "";
3435 
3436 	/* slow! */
3437 	if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3438 		text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
3439 	else
3440 		text[col_pos[S_COL_SIZE]] = "";
3441 
3442 	if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3443 		text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
3444 	else
3445 		text[col_pos[S_COL_SCORE]] = "";
3446 
3447 	if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
3448 		tags_text = procmsg_msginfo_get_tags_str(msginfo);
3449 		if (!tags_text) {
3450 			text[col_pos[S_COL_TAGS]] = "-";
3451 		} else {
3452 			strncpy2(tmp1, tags_text, sizeof(tmp1));
3453 			tmp1[sizeof(tmp1)-1]='\0';
3454 			g_free(tags_text);
3455 			text[col_pos[S_COL_TAGS]] = tmp1;
3456 		}
3457 	} else
3458 		text[col_pos[S_COL_TAGS]] = "";
3459 
3460 	/* slow! */
3461 	if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible ||
3462 	    ((vert_layout || small_layout) && prefs_common.two_line_vert)) {
3463 		if (msginfo->date_t && msginfo->date_t > 0) {
3464 			procheader_date_get_localtime(date_modified,
3465 						      sizeof(date_modified),
3466 						      msginfo->date_t);
3467 			text[col_pos[S_COL_DATE]] = date_modified;
3468 		} else if (msginfo->date)
3469 			text[col_pos[S_COL_DATE]] = msginfo->date;
3470 		else
3471 			text[col_pos[S_COL_DATE]] = _("(No Date)");
3472 	}
3473 
3474 	if (prefs_common.swap_from && msginfo->from && msginfo->to
3475 	&&  !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
3476 		gchar *addr = NULL;
3477 
3478 		addr = g_strdup(msginfo->from);
3479 
3480 		if (addr) {
3481 			extract_address(addr);
3482 			if (account_find_from_address(addr, FALSE)) {
3483 				should_swap = TRUE;
3484 			}
3485 			g_free(addr);
3486 		}
3487 	}
3488 
3489 	if (!prefs_common.use_addr_book) {
3490 		if (prefs_common.summary_from_show == SHOW_NAME)
3491 			from_text = msginfo->fromname;
3492 		else if (prefs_common.summary_from_show == SHOW_BOTH)
3493 			from_text = msginfo->from;
3494 		else {
3495 			from_text = msginfo->from;
3496 			extract_address(from_text);
3497 		}
3498 		if (!from_text)
3499 			from_text = _("(No From)");
3500 	} else {
3501 		gchar *tmp = summary_complete_address(msginfo->from);
3502 		if (tmp) {
3503 			strncpy2(from_buf, tmp, sizeof(from_buf));
3504 			g_free(tmp);
3505 			from_text = from_buf;
3506 		} else {
3507 			if (prefs_common.summary_from_show == SHOW_NAME)
3508 				from_text = msginfo->fromname;
3509 			else if (prefs_common.summary_from_show == SHOW_BOTH)
3510 				from_text = msginfo->from;
3511 			else {
3512 				from_text = msginfo->from;
3513 				if (from_text)
3514 					extract_address(from_text);
3515 			}
3516 			if (!from_text)
3517 				from_text = _("(No From)");
3518 		}
3519 	}
3520 
3521 	to_text = msginfo->to ? msginfo->to :
3522 		   (msginfo->cc ? msginfo->cc :
3523 		     (msginfo->newsgroups ? msginfo->newsgroups : NULL
3524 		     )
3525 		   );
3526 
3527 	if (!to_text)
3528 		to_text = _("(No Recipient)");
3529 	else {
3530 		if (prefs_common.summary_from_show == SHOW_NAME) {
3531 			gchar *tmp = procheader_get_fromname(to_text);
3532 			/* need to keep to_text pointing to stack, so heap-allocated
3533 			 * string from procheader_get_fromname() will be copied to to_buf */
3534 			if (tmp != NULL) {
3535 				strncpy2(to_buf, tmp, sizeof(to_buf));
3536 				g_free(tmp);
3537 				to_text = to_buf;
3538 			}
3539 		} else if (prefs_common.summary_from_show == SHOW_ADDR)
3540 			extract_address(to_text);
3541 	}
3542 
3543 	text[col_pos[S_COL_TO]] = to_text;
3544 	if (!should_swap) {
3545 		text[col_pos[S_COL_FROM]] = from_text;
3546 	} else {
3547 		if (prefs_common.use_addr_book) {
3548 			gchar *tmp = summary_complete_address(to_text);
3549 			/* need to keep to_text pointing to stack, so heap-allocated
3550 			 * string from summary_complete_address() will be copied to to_buf */
3551 			if (tmp) {
3552 				strncpy2(to_buf, tmp, sizeof(to_buf));
3553 				g_free(tmp);
3554 				to_text = to_buf;
3555 			} else {
3556 				to_text = to_text ? to_text : _("(No From)");
3557 			}
3558 		}
3559 		snprintf(tmp2, BUFFSIZE+4, "➜ %s", to_text);
3560 		tmp2[BUFFSIZE-1]='\0';
3561 		text[col_pos[S_COL_FROM]] = tmp2;
3562 	}
3563 
3564 	if (summaryview->simplify_subject_preg != NULL)
3565 		text[col_pos[S_COL_SUBJECT]] = msginfo->subject ?
3566 			string_remove_match(tmp3, BUFFSIZE, msginfo->subject,
3567 					summaryview->simplify_subject_preg) :
3568 			_("(No Subject)");
3569 	else
3570 		text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
3571 			_("(No Subject)");
3572 	if ((vert_layout || small_layout) && prefs_common.two_line_vert) {
3573 		if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) {
3574 			gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3575 									"<span color='%s' style='italic'>",
3576 									_("From: %s, on %s"), "</span>", NULL),
3577 					text[col_pos[S_COL_SUBJECT]],
3578 					color_dim_rgb,
3579 					text[col_pos[S_COL_FROM]],
3580 					text[col_pos[S_COL_DATE]]);
3581 			text[col_pos[S_COL_SUBJECT]] = tmp;
3582 		} else {
3583 			gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3584 									 "<span color='%s' style='italic'>",
3585 									_("To: %s, on %s"), "</span>", NULL),
3586 					text[col_pos[S_COL_SUBJECT]],
3587 					color_dim_rgb,
3588 					text[col_pos[S_COL_TO]],
3589 					text[col_pos[S_COL_DATE]]);
3590 			text[col_pos[S_COL_SUBJECT]] = tmp;
3591 		}
3592 	}
3593 }
3594 
summary_display_msg(SummaryView * summaryview,GtkCMCTreeNode * row)3595 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row)
3596 {
3597 	summary_display_msg_full(summaryview, row, FALSE, FALSE);
3598 }
3599 
3600 static gboolean defer_change(gpointer data);
3601 typedef enum {
3602 	FLAGS_UNSET,
3603 	FLAGS_SET,
3604 	FLAGS_CHANGE
3605 } ChangeType;
3606 typedef struct _ChangeData {
3607 	MsgInfo *info;
3608 	ChangeType op;
3609 	MsgPermFlags set_flags;
3610 	MsgTmpFlags  set_tmp_flags;
3611 	MsgPermFlags unset_flags;
3612 	MsgTmpFlags  unset_tmp_flags;
3613 } ChangeData;
3614 
summary_msginfo_unset_flags(MsgInfo * msginfo,MsgPermFlags flags,MsgTmpFlags tmp_flags)3615 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3616 {
3617 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
3618 		debug_print("flags: doing unset now\n");
3619 		procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
3620 	} else {
3621 		ChangeData *unset_data = g_new0(ChangeData, 1);
3622 		unset_data->info = msginfo;
3623 		unset_data->op = FLAGS_UNSET;
3624 		unset_data->unset_flags = flags;
3625 		unset_data->unset_tmp_flags = tmp_flags;
3626 		debug_print("flags: deferring unset\n");
3627 		g_timeout_add(100, defer_change, unset_data);
3628 	}
3629 }
3630 
summary_msginfo_set_flags(MsgInfo * msginfo,MsgPermFlags flags,MsgTmpFlags tmp_flags)3631 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3632 {
3633 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
3634 		debug_print("flags: doing set now\n");
3635 		procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
3636 	} else {
3637 		ChangeData *set_data = g_new0(ChangeData, 1);
3638 		set_data->info = msginfo;
3639 		set_data->op = FLAGS_SET;
3640 		set_data->set_flags = flags;
3641 		set_data->set_tmp_flags = tmp_flags;
3642 		debug_print("flags: deferring set\n");
3643 		g_timeout_add(100, defer_change, set_data);
3644 	}
3645 }
3646 
summary_msginfo_change_flags(MsgInfo * msginfo,MsgPermFlags add_flags,MsgTmpFlags add_tmp_flags,MsgPermFlags rem_flags,MsgTmpFlags rem_tmp_flags)3647 static void summary_msginfo_change_flags(MsgInfo *msginfo,
3648 		MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
3649 		MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
3650 {
3651 	if (!msginfo->folder || !msginfo->folder->processing_pending) {
3652 		debug_print("flags: doing change now\n");
3653 		procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
3654 			rem_flags, rem_tmp_flags);
3655 	} else {
3656 		ChangeData *change_data = g_new0(ChangeData, 1);
3657 		change_data->info = msginfo;
3658 		change_data->op = FLAGS_CHANGE;
3659 		change_data->set_flags = add_flags;
3660 		change_data->set_tmp_flags = add_tmp_flags;
3661 		change_data->unset_flags = rem_flags;
3662 		change_data->unset_tmp_flags = rem_tmp_flags;
3663 		debug_print("flags: deferring change\n");
3664 		g_timeout_add(100, defer_change, change_data);
3665 	}
3666 }
3667 
defer_change(gpointer data)3668 gboolean defer_change(gpointer data)
3669 {
3670 	ChangeData *chg = (ChangeData *)data;
3671 	if (chg->info->folder && chg->info->folder->processing_pending) {
3672 		debug_print("flags: trying later\n");
3673 		return TRUE; /* try again */
3674 	} else {
3675 		debug_print("flags: finally doing it\n");
3676 		switch(chg->op) {
3677 		case FLAGS_UNSET:
3678 			procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
3679 			break;
3680 		case FLAGS_SET:
3681 			procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
3682 			break;
3683 		case FLAGS_CHANGE:
3684 			procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
3685 				chg->unset_flags, chg->unset_tmp_flags);
3686 			break;
3687 		default:
3688 			g_warning("unknown change op");
3689 		}
3690 		g_free(chg);
3691 	}
3692 	return FALSE;
3693 }
3694 
msginfo_mark_as_read(SummaryView * summaryview,MsgInfo * msginfo,GtkCMCTreeNode * row)3695 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
3696 				      GtkCMCTreeNode *row)
3697 {
3698 	cm_return_if_fail(summaryview != NULL);
3699 	cm_return_if_fail(msginfo != NULL);
3700 	cm_return_if_fail(row != NULL);
3701 
3702 	if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3703 		summary_msginfo_unset_flags
3704 			(msginfo, MSG_NEW | MSG_UNREAD, 0);
3705 		summary_set_row_marks(summaryview, row);
3706 		summary_status_show(summaryview);
3707 	}
3708 }
3709 
3710 typedef struct  {
3711 	MsgInfo *msginfo;
3712 	SummaryView *summaryview;
3713 } MarkAsReadData;
3714 
msginfo_mark_as_read_timeout(void * data)3715 static int msginfo_mark_as_read_timeout(void *data)
3716 {
3717 	MarkAsReadData *mdata = (MarkAsReadData *)data;
3718 	if (!mdata)
3719 		return FALSE;
3720 
3721 	summary_lock(mdata->summaryview);
3722 	if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
3723 		msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
3724 				     mdata->summaryview->selected);
3725 	procmsg_msginfo_free(&(mdata->msginfo));
3726 
3727 	mdata->summaryview->mark_as_read_timeout_tag = 0;
3728 	summary_unlock(mdata->summaryview);
3729 
3730 	g_free(mdata);
3731 	return FALSE;
3732 }
3733 
summary_display_msg_full(SummaryView * summaryview,GtkCMCTreeNode * row,gboolean new_window,gboolean all_headers)3734 static void summary_display_msg_full(SummaryView *summaryview,
3735 				     GtkCMCTreeNode *row,
3736 				     gboolean new_window, gboolean all_headers)
3737 {
3738 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3739 	MsgInfo *msginfo;
3740 	gint val;
3741 	START_TIMING("");
3742 	if (!new_window) {
3743 		if (summaryview->displayed == row &&
3744 		    messageview_is_visible(summaryview->messageview))
3745 			return;
3746 		else if (summaryview->messageview)
3747 			summaryview->messageview->filtered = FALSE;
3748 	}
3749 	cm_return_if_fail(row != NULL);
3750 
3751 	if (summary_is_locked(summaryview)) return;
3752 	summary_lock(summaryview);
3753 
3754 	STATUSBAR_POP(summaryview->mainwin);
3755 	GTK_EVENTS_FLUSH();
3756 
3757 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3758 
3759 	if (!msginfo) {
3760 		debug_print("NULL msginfo\n");
3761 		summary_unlock(summaryview);
3762 		END_TIMING();
3763 		return;
3764 	}
3765 
3766 	if (new_window && prefs_common.layout_mode != SMALL_LAYOUT) {
3767 		MessageView *msgview;
3768 
3769 		msgview = messageview_create_with_new_window(summaryview->mainwin);
3770 		val = messageview_show(msgview, msginfo, all_headers);
3771 	} else {
3772 		MessageView *msgview;
3773 
3774 		if (prefs_common.layout_mode == SMALL_LAYOUT) {
3775 			if (summaryview->ext_messageview == NULL)
3776 				summaryview->ext_messageview = messageview_create_with_new_window(summaryview->mainwin);
3777 			else
3778 				gtkut_window_popup(summaryview->ext_messageview->window);
3779 			msgview = summaryview->ext_messageview;
3780 			summaryview->displayed = row;
3781 			val = messageview_show(msgview, msginfo, all_headers);
3782 			if (mimeview_tree_is_empty(msgview->mimeview))
3783 				gtk_widget_grab_focus(summaryview->ctree);
3784 			gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3785 				GTK_CMCLIST(summaryview->ctree)->focus_row);
3786 		} else {
3787 			msgview = summaryview->messageview;
3788 			summaryview->displayed = row;
3789 			if (!messageview_is_visible(msgview) &&
3790 			    gtk_window_is_active(GTK_WINDOW(summaryview->mainwin->window))) {
3791 				main_window_toggle_message_view(summaryview->mainwin);
3792 				GTK_EVENTS_FLUSH();
3793 			}
3794 			val = messageview_show(msgview, msginfo, all_headers);
3795 			if (mimeview_tree_is_empty(msgview->mimeview))
3796 				gtk_widget_grab_focus(summaryview->ctree);
3797 			gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3798 				GTK_CMCLIST(summaryview->ctree)->focus_row);
3799 		}
3800 	}
3801 
3802 	if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
3803 		if (!prefs_common.mark_as_read_on_new_window &&
3804 		    prefs_common.mark_as_read_delay) {
3805 			MarkAsReadData *data = g_new0(MarkAsReadData, 1);
3806 			data->summaryview = summaryview;
3807 			data->msginfo = procmsg_msginfo_new_ref(msginfo);
3808 			if (summaryview->mark_as_read_timeout_tag != 0)
3809 				g_source_remove(summaryview->mark_as_read_timeout_tag);
3810 			summaryview->mark_as_read_timeout_tag =
3811 				g_timeout_add_seconds(prefs_common.mark_as_read_delay,
3812 					msginfo_mark_as_read_timeout, data);
3813 		} else if (new_window || !prefs_common.mark_as_read_on_new_window) {
3814 			msginfo_mark_as_read(summaryview, msginfo, row);
3815 		}
3816 	}
3817 
3818 	summary_set_menu_sensitive(summaryview);
3819 	toolbar_main_set_sensitive(summaryview->mainwin);
3820 	messageview_set_menu_sensitive(summaryview->messageview);
3821 
3822 	summary_unlock(summaryview);
3823 	END_TIMING();
3824 }
3825 
summary_display_msg_selected(SummaryView * summaryview,gboolean all_headers)3826 void summary_display_msg_selected(SummaryView *summaryview,
3827 				  gboolean all_headers)
3828 {
3829 	if (summary_is_locked(summaryview)) return;
3830 	summaryview->displayed = NULL;
3831 	summary_display_msg_full(summaryview, summaryview->selected, FALSE,
3832 				 all_headers);
3833 }
3834 
summary_redisplay_msg(SummaryView * summaryview)3835 void summary_redisplay_msg(SummaryView *summaryview)
3836 {
3837 	GtkCMCTreeNode *node;
3838 
3839 	if (summaryview->displayed) {
3840 		node = summaryview->displayed;
3841 		summaryview->displayed = NULL;
3842 		summary_display_msg(summaryview, node);
3843 	}
3844 }
3845 
summary_open_msg(SummaryView * summaryview)3846 void summary_open_msg(SummaryView *summaryview)
3847 {
3848 	if (!summaryview->selected) return;
3849 
3850 	/* CLAWS: if separate message view, don't open a new window
3851 	 * but rather use the current separated message view */
3852 	summary_display_msg_full(summaryview, summaryview->selected,
3853 				 TRUE, FALSE);
3854 }
3855 
summary_view_source(SummaryView * summaryview)3856 void summary_view_source(SummaryView * summaryview)
3857 {
3858 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3859 	MsgInfo *msginfo;
3860 	SourceWindow *srcwin;
3861 
3862 	if (!summaryview->selected) return;
3863 
3864 	srcwin = source_window_create();
3865 	msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
3866 	source_window_show_msg(srcwin, msginfo);
3867 	source_window_show(srcwin);
3868 }
3869 
summary_reedit(SummaryView * summaryview)3870 void summary_reedit(SummaryView *summaryview)
3871 {
3872 	MsgInfo *msginfo;
3873 
3874 	if (!summaryview->selected) return;
3875 	if (!summaryview->folder_item) return;
3876 	if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
3877 		return;
3878 
3879 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
3880 					      summaryview->selected);
3881 	if (!msginfo) return;
3882 
3883 	compose_reedit(msginfo, FALSE);
3884 }
3885 
summary_is_list(SummaryView * summaryview)3886 gboolean summary_is_list(SummaryView *summaryview)
3887 {
3888 	return (gtk_notebook_get_current_page(
3889 		GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0);
3890 }
3891 
summary_toggle_view(SummaryView * summaryview)3892 void summary_toggle_view(SummaryView *summaryview)
3893 {
3894 	if (prefs_common.layout_mode == SMALL_LAYOUT)
3895 		return;
3896 	if (summary_is_locked(summaryview))
3897 		return;
3898 	if (!messageview_is_visible(summaryview->messageview) &&
3899 	    summaryview->selected && summary_is_list(summaryview))
3900 		summary_display_msg(summaryview,
3901 				    summaryview->selected);
3902 	else
3903 		main_window_toggle_message_view(summaryview->mainwin);
3904 }
3905 
summary_search_unread_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3906 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree,
3907 						GtkCMCTreeNode *node)
3908 {
3909 	MsgInfo *msginfo;
3910 
3911 	if (node) {
3912 		msginfo = gtk_cmctree_node_get_row_data(ctree, node);
3913 		if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3914 			return TRUE;
3915 		node = GTK_CMCTREE_ROW(node)->children;
3916 	} else
3917 		node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3918 
3919 	while (node) {
3920 		if (summary_search_unread_recursive(ctree, node) == TRUE)
3921 			return TRUE;
3922 		node = GTK_CMCTREE_ROW(node)->sibling;
3923 	}
3924 
3925 	return FALSE;
3926 }
3927 
summary_have_unread_children(SummaryView * summaryview,GtkCMCTreeNode * node)3928 static gboolean summary_have_unread_children(SummaryView *summaryview,
3929 					     GtkCMCTreeNode *node)
3930 {
3931 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3932 
3933 	if (!node) return FALSE;
3934 
3935 	node = GTK_CMCTREE_ROW(node)->children;
3936 
3937 	while (node) {
3938 		if (summary_search_unread_recursive(ctree, node) == TRUE)
3939 			return TRUE;
3940 		node = GTK_CMCTREE_ROW(node)->sibling;
3941 	}
3942 	return FALSE;
3943 }
3944 
summary_set_row_marks(SummaryView * summaryview,GtkCMCTreeNode * row)3945 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row)
3946 {
3947 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3948 	GtkStyle *style = NULL;
3949 	MsgInfo *msginfo;
3950 	MsgFlags flags;
3951 	gint *col_pos = summaryview->col_pos;
3952 
3953 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3954 	if (!msginfo) return;
3955 
3956 	flags = msginfo->flags;
3957 
3958 	gtk_cmctree_node_set_foreground(ctree, row, NULL);
3959 
3960 	/* set new/unread column */
3961 	if (MSG_IS_IGNORE_THREAD(flags)) {
3962 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3963 					  ignorethreadxpm);
3964 	} else if (MSG_IS_WATCH_THREAD(flags)) {
3965 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3966 					  watchthreadxpm);
3967 	} else if (MSG_IS_SPAM(flags)) {
3968 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3969 					  spamxpm);
3970 	} else if (MSG_IS_NEW(flags)) {
3971 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3972 					  newxpm);
3973 	} else if (MSG_IS_UNREAD(flags)) {
3974 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3975 					  unreadxpm);
3976 	} else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
3977 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3978 					  repliedandforwardedxpm);
3979 	} else if (MSG_IS_REPLIED(flags)) {
3980 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3981 					  repliedxpm);
3982 	} else if (MSG_IS_FORWARDED(flags)) {
3983 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3984 					  forwardedxpm);
3985 	} else {
3986 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
3987 					"");
3988 	}
3989 
3990 	if (prefs_common.bold_unread &&
3991 	    ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
3992 	     (!GTK_CMCTREE_ROW(row)->expanded &&
3993 	      GTK_CMCTREE_ROW(row)->children &&
3994 	      summary_have_unread_children(summaryview, row))))
3995 		style = bold_style;
3996 
3997 	/* set mark column */
3998 	if (MSG_IS_DELETED(flags)) {
3999 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4000 					  deletedxpm);
4001 		if (style)
4002 			style = bold_deleted_style;
4003 		else {
4004 			style = small_deleted_style;
4005 		}
4006 			gtk_cmctree_node_set_foreground
4007 				(ctree, row, &summaryview->color_dim);
4008 	} else if (MSG_IS_MARKED(flags)) {
4009 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4010 					  markxpm);
4011 	} else if (MSG_IS_MOVE(flags)) {
4012 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4013 					  movedxpm);
4014 		if (!msginfo->to_folder ||
4015 		    !folder_has_parent_of_type(msginfo->to_folder, F_TRASH)) {
4016 			if (style)
4017 				style = bold_marked_style;
4018 			else {
4019 				style = small_marked_style;
4020 			}
4021 			gtk_cmctree_node_set_foreground
4022 				(ctree, row, &summaryview->color_marked);
4023 		} else {
4024 			if (style)
4025 				style = bold_deleted_style;
4026 			else {
4027 				style = small_deleted_style;
4028 			}
4029 				gtk_cmctree_node_set_foreground
4030 					(ctree, row, &summaryview->color_dim);
4031 		}
4032 	} else if (MSG_IS_COPY(flags)) {
4033 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4034 					  copiedxpm);
4035 		if (style)
4036 			style = bold_marked_style;
4037 		else {
4038 			style = small_marked_style;
4039 		}
4040 			gtk_cmctree_node_set_foreground
4041                         	(ctree, row, &summaryview->color_marked);
4042 	} else {
4043 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
4044 	}
4045 
4046 	if (MSG_IS_LOCKED(flags)) {
4047 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_LOCKED],
4048 					  lockedxpm);
4049 	}
4050 	else {
4051 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
4052 	}
4053 
4054 	if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
4055 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4056 					  clipgpgsignedxpm);
4057 	} else if (MSG_IS_SIGNED(flags)) {
4058 		if (MSG_IS_ENCRYPTED(flags)) {
4059 			gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4060 					  keysignxpm);
4061 		} else {
4062 			gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4063 						  gpgsignedxpm);
4064 		}
4065 	} else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
4066 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4067 					  clipkeyxpm);
4068 	} else if (MSG_IS_ENCRYPTED(flags)) {
4069 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4070 					  keyxpm);
4071 	} else if (MSG_IS_WITH_ATTACHMENT(flags)) {
4072 		gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4073 					  clipxpm);
4074 	} else {
4075 		gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
4076 	}
4077 	if (!style)
4078 		style = small_style;
4079 
4080 	gtk_cmctree_node_set_row_style(ctree, row, style);
4081 
4082 	if (MSG_GET_COLORLABEL(flags))
4083 		summary_set_colorlabel_color(ctree, row, MSG_GET_COLORLABEL_VALUE(flags));
4084 }
4085 
summary_mark_row(SummaryView * summaryview,GtkCMCTreeNode * row)4086 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4087 {
4088 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4089 	MsgInfo *msginfo;
4090 
4091 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4092 	cm_return_if_fail(msginfo);
4093 	if (MSG_IS_DELETED(msginfo->flags))
4094 		summaryview->deleted--;
4095 	if (MSG_IS_MOVE(msginfo->flags))
4096 		summaryview->moved--;
4097 	if (MSG_IS_COPY(msginfo->flags))
4098 		summaryview->copied--;
4099 
4100 	procmsg_msginfo_set_to_folder(msginfo, NULL);
4101 	summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED,
4102 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4103 	summary_set_row_marks(summaryview, row);
4104 	debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
4105 }
4106 
summary_lock_row(SummaryView * summaryview,GtkCMCTreeNode * row)4107 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4108 {
4109 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4110 	MsgInfo *msginfo;
4111 
4112 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4113 	cm_return_if_fail(msginfo);
4114 	if (MSG_IS_DELETED(msginfo->flags))
4115 		summaryview->deleted--;
4116 	if (MSG_IS_MOVE(msginfo->flags)) {
4117 		summaryview->moved--;
4118 	}
4119 	if (MSG_IS_COPY(msginfo->flags)) {
4120 		summaryview->copied--;
4121 	}
4122 	procmsg_msginfo_set_to_folder(msginfo, NULL);
4123 	summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED,
4124 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4125 
4126 	summary_set_row_marks(summaryview, row);
4127 	debug_print("Message %d is locked\n", msginfo->msgnum);
4128 }
4129 
summary_unlock_row(SummaryView * summaryview,GtkCMCTreeNode * row)4130 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4131 {
4132 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4133 	MsgInfo *msginfo;
4134 
4135 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4136 	cm_return_if_fail(msginfo);
4137 	if (!MSG_IS_LOCKED(msginfo->flags))
4138 		return;
4139 	procmsg_msginfo_set_to_folder(msginfo, NULL);
4140 	summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
4141 	summary_set_row_marks(summaryview, row);
4142 	debug_print("Message %d is unlocked\n", msginfo->msgnum);
4143 }
4144 
summary_mark(SummaryView * summaryview)4145 void summary_mark(SummaryView *summaryview)
4146 {
4147 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4148 	GList *cur;
4149 	gboolean froze = FALSE;
4150 
4151 	if (summary_is_locked(summaryview))
4152 		return;
4153 	START_LONG_OPERATION(summaryview, FALSE);
4154 	folder_item_set_batch(summaryview->folder_item, TRUE);
4155 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4156 		summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4157 	folder_item_set_batch(summaryview->folder_item, FALSE);
4158 	END_LONG_OPERATION(summaryview);
4159 
4160 	summary_status_show(summaryview);
4161 }
4162 
summary_mark_row_as_read(SummaryView * summaryview,GtkCMCTreeNode * row)4163 static void summary_mark_row_as_read(SummaryView *summaryview,
4164 				     GtkCMCTreeNode *row)
4165 {
4166 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4167 	MsgInfo *msginfo;
4168 
4169 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4170 	cm_return_if_fail(msginfo);
4171 
4172 	if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
4173 		return;
4174 
4175 	summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
4176 	summary_set_row_marks(summaryview, row);
4177 	debug_print("Message %d is marked as read\n",
4178 		msginfo->msgnum);
4179 }
4180 
summary_mark_row_as_unread(SummaryView * summaryview,GtkCMCTreeNode * row)4181 static void summary_mark_row_as_unread(SummaryView *summaryview,
4182 				     GtkCMCTreeNode *row)
4183 {
4184 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4185 	MsgInfo *msginfo;
4186 
4187 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4188 	cm_return_if_fail(msginfo);
4189 
4190 	if(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))
4191 		return;
4192 
4193 	summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
4194 	summary_set_row_marks(summaryview, row);
4195 	debug_print("Message %d is marked as unread\n",
4196 		msginfo->msgnum);
4197 }
4198 
summary_mark_as_read(SummaryView * summaryview)4199 void summary_mark_as_read(SummaryView *summaryview)
4200 {
4201 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4202 	GList *cur;
4203 	gboolean froze = FALSE;
4204 
4205 	if (summary_is_locked(summaryview))
4206 		return;
4207 
4208 	if (!summaryview->folder_item)
4209 		return;
4210 
4211 	if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
4212 	     summaryview->folder_item->total_msgs > 1) &&
4213 	    !summary_mark_all_read_confirm(TRUE))
4214 		return;
4215 
4216 	START_LONG_OPERATION(summaryview, FALSE);
4217 	folder_item_set_batch(summaryview->folder_item, TRUE);
4218 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4219 		summary_mark_row_as_read(summaryview,
4220 					 GTK_CMCTREE_NODE(cur->data));
4221 	folder_item_set_batch(summaryview->folder_item, FALSE);
4222 	END_LONG_OPERATION(summaryview);
4223 
4224 	summary_status_show(summaryview);
4225 }
4226 
summary_mark_as_unread(SummaryView * summaryview)4227 void summary_mark_as_unread(SummaryView *summaryview)
4228 {
4229 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4230 	GList *cur;
4231 	gboolean froze = FALSE;
4232 
4233 	if (summary_is_locked(summaryview))
4234 		return;
4235 
4236 	if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
4237 	     summaryview->folder_item->total_msgs > 1) &&
4238 	    !summary_mark_all_unread_confirm(TRUE))
4239 		return;
4240 
4241 	START_LONG_OPERATION(summaryview, FALSE);
4242 	folder_item_set_batch(summaryview->folder_item, TRUE);
4243 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4244 		summary_mark_row_as_unread(summaryview,
4245 					 GTK_CMCTREE_NODE(cur->data));
4246 	folder_item_set_batch(summaryview->folder_item, FALSE);
4247 	END_LONG_OPERATION(summaryview);
4248 
4249 	summary_status_show(summaryview);
4250 }
4251 
summary_msgs_lock(SummaryView * summaryview)4252 void summary_msgs_lock(SummaryView *summaryview)
4253 {
4254 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4255 	GList *cur;
4256 	gboolean froze = FALSE;
4257 
4258 	if (summary_is_locked(summaryview))
4259 		return;
4260 	START_LONG_OPERATION(summaryview, FALSE);
4261 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4262 		summary_lock_row(summaryview,
4263 					 GTK_CMCTREE_NODE(cur->data));
4264 	END_LONG_OPERATION(summaryview);
4265 
4266 	summary_status_show(summaryview);
4267 }
4268 
summary_msgs_unlock(SummaryView * summaryview)4269 void summary_msgs_unlock(SummaryView *summaryview)
4270 {
4271 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4272 	GList *cur;
4273 	gboolean froze = FALSE;
4274 
4275 	if (summary_is_locked(summaryview))
4276 		return;
4277 	START_LONG_OPERATION(summaryview, FALSE);
4278 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4279 		summary_unlock_row(summaryview,
4280 				   GTK_CMCTREE_NODE(cur->data));
4281 	END_LONG_OPERATION(summaryview);
4282 
4283 	summary_status_show(summaryview);
4284 }
4285 
summary_mark_all_read_confirm(gboolean ask_if_needed)4286 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed)
4287 {
4288 	/* ask_if_needed is FALSE when user-asking is performed by caller,
4289 	   commonly when the caller is a mark-as-read-recursive func */
4290 	if (ask_if_needed && prefs_common.ask_mark_all_read) {
4291 		AlertValue val = alertpanel_full(_("Mark all as read"),
4292 			  _("Do you really want to mark all mails in this folder as read?"),
4293 			  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
4294 			  TRUE, NULL, ALERT_QUESTION);
4295 
4296 		if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4297 			return FALSE;
4298 		else if (val & G_ALERTDISABLE)
4299 			prefs_common.ask_mark_all_read = FALSE;
4300 	}
4301 	return TRUE;
4302 }
4303 
summary_mark_all_read(SummaryView * summaryview,gboolean ask_if_needed)4304 void summary_mark_all_read(SummaryView *summaryview, gboolean ask_if_needed)
4305 {
4306 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4307 	GtkCMCTreeNode *node;
4308 	gboolean froze = FALSE;
4309 
4310 	if (summary_is_locked(summaryview))
4311 		return;
4312 
4313 	if (!summary_mark_all_read_confirm(ask_if_needed))
4314 		return;
4315 
4316 	START_LONG_OPERATION(summaryview, TRUE);
4317 	folder_item_set_batch(summaryview->folder_item, TRUE);
4318 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4319 		node = gtkut_ctree_node_next(ctree, node))
4320 		summary_mark_row_as_read(summaryview, node);
4321 	folder_item_set_batch(summaryview->folder_item, FALSE);
4322 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4323 		node = gtkut_ctree_node_next(ctree, node)) {
4324 		if (!GTK_CMCTREE_ROW(node)->expanded)
4325 			summary_set_row_marks(summaryview, node);
4326 	}
4327 	END_LONG_OPERATION(summaryview);
4328 
4329 	summary_status_show(summaryview);
4330 }
4331 
summary_mark_all_unread_confirm(gboolean ask_if_needed)4332 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed)
4333 {
4334 	/* ask_if_needed is FALSE when user-asking is performed by caller,
4335 	   commonly when the caller is a mark-as-unread-recursive func */
4336 	if (ask_if_needed && prefs_common.ask_mark_all_read) {
4337 		AlertValue val = alertpanel_full(_("Mark all as unread"),
4338 			  _("Do you really want to mark all mails in this folder as unread?"),
4339 			  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
4340 			  TRUE, NULL, ALERT_QUESTION);
4341 
4342 		if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4343 			return FALSE;
4344 		else if (val & G_ALERTDISABLE)
4345 			prefs_common.ask_mark_all_read = FALSE;
4346 	}
4347 	return TRUE;
4348 }
4349 
summary_mark_all_unread(SummaryView * summaryview,gboolean ask_if_needed)4350 void summary_mark_all_unread(SummaryView *summaryview, gboolean ask_if_needed)
4351 {
4352 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4353 	GtkCMCTreeNode *node;
4354 	gboolean froze = FALSE;
4355 
4356 	if (summary_is_locked(summaryview))
4357 		return;
4358 
4359 	if (!summary_mark_all_unread_confirm(ask_if_needed))
4360 		return;
4361 
4362 	START_LONG_OPERATION(summaryview, TRUE);
4363 	folder_item_set_batch(summaryview->folder_item, TRUE);
4364 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4365 		node = gtkut_ctree_node_next(ctree, node))
4366 		summary_mark_row_as_unread(summaryview, node);
4367 	folder_item_set_batch(summaryview->folder_item, FALSE);
4368 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4369 		node = gtkut_ctree_node_next(ctree, node)) {
4370 		if (!GTK_CMCTREE_ROW(node)->expanded)
4371 			summary_set_row_marks(summaryview, node);
4372 	}
4373 	END_LONG_OPERATION(summaryview);
4374 
4375 	summary_status_show(summaryview);
4376 }
4377 
summary_mark_as_spam(SummaryView * summaryview,guint action,GtkWidget * widget)4378 void summary_mark_as_spam(SummaryView *summaryview, guint action, GtkWidget *widget)
4379 {
4380 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4381 	GList *cur;
4382 	gboolean is_spam = action;
4383 	GSList *msgs = NULL;
4384 	gboolean immediate_exec = prefs_common.immediate_exec;
4385 	gboolean moved = FALSE;
4386 	gboolean froze = FALSE;
4387 
4388 
4389 	if (summary_is_locked(summaryview))
4390 		return;
4391 
4392 	prefs_common.immediate_exec = FALSE;
4393 	START_LONG_OPERATION(summaryview, FALSE);
4394 	folder_item_set_batch(summaryview->folder_item, TRUE);
4395 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4396 		GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4397 		MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4398 		if (msginfo)
4399 			msgs = g_slist_prepend(msgs, msginfo);
4400 	}
4401 
4402 	if (procmsg_spam_learner_learn(NULL, msgs, is_spam) == 0) {
4403 		for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4404 			GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4405 			MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4406 			if (!msginfo)
4407 				continue;
4408 			if (is_spam) {
4409 				summary_msginfo_change_flags(msginfo, MSG_SPAM, 0, MSG_NEW|MSG_UNREAD, 0);
4410 				if (procmsg_spam_get_folder(msginfo) != summaryview->folder_item) {
4411 					summary_move_row_to(summaryview, row,
4412 							procmsg_spam_get_folder(msginfo));
4413 					moved = TRUE;
4414 				}
4415 			} else {
4416 				summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
4417 			}
4418 			summaryview->display_msg = prefs_common.always_show_msg;
4419 
4420 			summary_set_row_marks(summaryview, row);
4421 		}
4422 	} else {
4423 		log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
4424 	}
4425 
4426 	prefs_common.immediate_exec = immediate_exec;
4427 	folder_item_set_batch(summaryview->folder_item, FALSE);
4428 	END_LONG_OPERATION(summaryview);
4429 
4430 	if (prefs_common.immediate_exec && moved) {
4431 		summary_execute(summaryview);
4432 	}
4433 
4434 	if (!moved && msgs) {
4435 		MsgInfo *msginfo = (MsgInfo *)msgs->data;
4436 		toolbar_set_learn_button
4437 			(summaryview->mainwin->toolbar,
4438 			 MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
4439 	}
4440 	g_slist_free(msgs);
4441 
4442 	summary_status_show(summaryview);
4443 }
4444 
check_permission(SummaryView * summaryview,MsgInfo * msginfo)4445 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
4446 {
4447 	GList * cur;
4448 	gboolean found;
4449 
4450 	switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
4451 
4452 	case F_NEWS:
4453 
4454 		/*
4455 		  security : checks if one the accounts correspond to
4456 		  the author of the post
4457 		*/
4458 
4459 		found = FALSE;
4460 		for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
4461 			PrefsAccount * account;
4462 			gchar * from_name;
4463 
4464 			account = cur->data;
4465 			if (account->name && *account->name)
4466 				from_name =
4467 					g_strdup_printf("%s <%s>",
4468 							account->name,
4469 							account->address);
4470 			else
4471 				from_name =
4472 					g_strdup_printf("%s",
4473 							account->address);
4474 
4475 			if (g_utf8_collate(from_name, msginfo->from) == 0) {
4476 				g_free(from_name);
4477 				found = TRUE;
4478 				break;
4479 			}
4480 			g_free(from_name);
4481 		}
4482 
4483 		if (!found) {
4484 			alertpanel_error(_("You're not the author of the article."));
4485 		}
4486 
4487 		return found;
4488 
4489 	default:
4490 		return TRUE;
4491 	}
4492 }
4493 
summary_delete_row(SummaryView * summaryview,GtkCMCTreeNode * row)4494 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4495 {
4496 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4497 	MsgInfo *msginfo;
4498 
4499 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4500 	cm_return_if_fail(msginfo);
4501 
4502 	if (MSG_IS_LOCKED(msginfo->flags)) return;
4503 
4504 	if (MSG_IS_DELETED(msginfo->flags)) return;
4505 
4506 	if (MSG_IS_MOVE(msginfo->flags))
4507 		summaryview->moved--;
4508 	if (MSG_IS_COPY(msginfo->flags))
4509 		summaryview->copied--;
4510 
4511 	procmsg_msginfo_set_to_folder(msginfo, NULL);
4512 	summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED,
4513 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4514 	summaryview->deleted++;
4515 
4516 	if (!prefs_common.immediate_exec &&
4517 	    !folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) {
4518 		summary_set_row_marks(summaryview, row);
4519 	}
4520 	debug_print("Message %s/%d is set to delete\n",
4521 		    msginfo->folder->path, msginfo->msgnum);
4522 }
4523 
summary_cancel(SummaryView * summaryview)4524 void summary_cancel(SummaryView *summaryview)
4525 {
4526 	MsgInfo * msginfo;
4527 
4528 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4529 					      summaryview->selected);
4530 	if (!msginfo) return;
4531 
4532 	if (!check_permission(summaryview, msginfo))
4533 		return;
4534 
4535 	news_cancel_article(summaryview->folder_item->folder, msginfo);
4536 
4537 	if (summary_is_locked(summaryview)) return;
4538 
4539 	summary_lock(summaryview);
4540 
4541 	summary_freeze(summaryview);
4542 
4543 	summary_update_status(summaryview);
4544 	summary_status_show(summaryview);
4545 
4546 	summary_thaw(summaryview);
4547 
4548 	summary_unlock(summaryview);
4549 }
4550 
summary_delete(SummaryView * summaryview)4551 void summary_delete(SummaryView *summaryview)
4552 {
4553 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4554 	FolderItem *item = summaryview->folder_item;
4555 	GList *cur;
4556 	GtkCMCTreeNode *sel_last = NULL;
4557 	GtkCMCTreeNode *node;
4558 	AlertValue aval;
4559 	MsgInfo *msginfo;
4560 	gboolean froze = FALSE;
4561 
4562 	if (!item) return;
4563 
4564 	if (summary_is_locked(summaryview)) return;
4565 
4566 	if (!summaryview->folder_item) return;
4567 
4568 	START_LONG_OPERATION(summaryview, FALSE);
4569 
4570 	if (!prefs_common.live_dangerously) {
4571 		gchar *buf = NULL;
4572 		guint num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
4573 		buf = g_strdup_printf(ngettext(
4574 			"Do you really want to delete the selected message?",
4575 			"Do you really want to delete the %d selected messages?", num),
4576 			num);
4577 		aval = alertpanel(ngettext("Delete message", "Delete messages", num),
4578 				  buf,
4579 				  GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, ALERTFOCUS_SECOND);
4580 		g_free(buf);
4581 		if (aval != G_ALERTALTERNATE) {
4582 			END_LONG_OPERATION(summaryview);
4583 			return;
4584 		}
4585 	}
4586 
4587 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL;
4588 	     cur = cur->next) {
4589 		GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4590 		msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4591 		if (msginfo && msginfo->total_size != 0 &&
4592 		    msginfo->size != (goffset)msginfo->total_size)
4593 			partial_mark_for_delete(msginfo);
4594 	}
4595 
4596 	main_window_cursor_wait(summaryview->mainwin);
4597 
4598 	/* next code sets current row focus right. We need to find a row
4599 	 * that is not deleted. */
4600 	folder_item_set_batch(summaryview->folder_item, TRUE);
4601 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4602 		sel_last = GTK_CMCTREE_NODE(cur->data);
4603 		summary_delete_row(summaryview, sel_last);
4604 	}
4605 	folder_item_set_batch(summaryview->folder_item, FALSE);
4606 	END_LONG_OPERATION(summaryview);
4607 
4608 	if (summaryview->sort_type == SORT_ASCENDING) {
4609 		node = summary_find_next_msg(summaryview, sel_last, TRUE);
4610 		if (!node || prefs_common.next_on_delete == FALSE)
4611 			node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4612 	} else {
4613 		node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4614 		if (!node || prefs_common.next_on_delete == FALSE)
4615 			node = summary_find_next_msg(summaryview, sel_last,TRUE);
4616 	}
4617 	summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4618 
4619 	if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
4620 		summary_execute(summaryview);
4621 		/* after deleting, the anchor may be at an invalid row
4622 		 * so reset it to the node we found earlier */
4623 		gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
4624 	} else
4625 		summary_status_show(summaryview);
4626 
4627 
4628 	main_window_cursor_normal(summaryview->mainwin);
4629 }
4630 
summary_delete_trash(SummaryView * summaryview)4631 void summary_delete_trash(SummaryView *summaryview)
4632 {
4633 	FolderItem *to_folder = NULL;
4634 	PrefsAccount *ac;
4635 	if (!summaryview->folder_item ||
4636 	    FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4637 
4638 	if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
4639 		to_folder = account_get_special_folder(ac, F_TRASH);
4640 
4641 	if (to_folder == NULL)
4642 		to_folder = summaryview->folder_item->folder->trash;
4643 
4644 	if (to_folder == NULL || to_folder == summaryview->folder_item
4645 	    || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
4646 		summary_delete(summaryview);
4647 	else
4648 		summary_move_selected_to(summaryview, to_folder);
4649 }
4650 
4651 
summary_unmark_row(SummaryView * summaryview,GtkCMCTreeNode * row)4652 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4653 {
4654 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4655 	MsgInfo *msginfo;
4656 
4657 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4658 	cm_return_if_fail(msginfo);
4659 	if (MSG_IS_DELETED(msginfo->flags))
4660 		summaryview->deleted--;
4661 	if (MSG_IS_MOVE(msginfo->flags))
4662 		summaryview->moved--;
4663 	if (MSG_IS_COPY(msginfo->flags))
4664 		summaryview->copied--;
4665 
4666 	procmsg_msginfo_set_to_folder(msginfo, NULL);
4667 	summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED,
4668 		MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4669 	summary_set_row_marks(summaryview, row);
4670 
4671 	debug_print("Message %s/%d is unmarked\n",
4672 		    msginfo->folder->path, msginfo->msgnum);
4673 }
4674 
summary_unmark(SummaryView * summaryview)4675 void summary_unmark(SummaryView *summaryview)
4676 {
4677 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4678 	GList *cur;
4679 	gboolean froze = FALSE;
4680 
4681 	if (summary_is_locked(summaryview))
4682 		return;
4683 	START_LONG_OPERATION(summaryview, FALSE);
4684 	folder_item_set_batch(summaryview->folder_item, TRUE);
4685 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4686 		summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4687 	folder_item_set_batch(summaryview->folder_item, FALSE);
4688 	END_LONG_OPERATION(summaryview);
4689 
4690 	summary_status_show(summaryview);
4691 }
4692 
summary_move_row_to(SummaryView * summaryview,GtkCMCTreeNode * row,FolderItem * to_folder)4693 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4694 				FolderItem *to_folder)
4695 {
4696 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4697 	MsgInfo *msginfo;
4698 
4699 	cm_return_if_fail(to_folder != NULL);
4700 
4701 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4702 	cm_return_if_fail(msginfo);
4703 	if (MSG_IS_LOCKED(msginfo->flags))
4704 		return;
4705 
4706 	procmsg_msginfo_set_to_folder(msginfo, to_folder);
4707 	if (MSG_IS_DELETED(msginfo->flags))
4708 		summaryview->deleted--;
4709 	if (MSG_IS_COPY(msginfo->flags)) {
4710 		summaryview->copied--;
4711 	}
4712 	if (!MSG_IS_MOVE(msginfo->flags)) {
4713 		summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED,
4714 			MSG_COPY | MSG_MOVE_DONE);
4715 		summaryview->moved++;
4716 	} else {
4717 		summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
4718 	}
4719 
4720 	if (!prefs_common.immediate_exec) {
4721 		summary_set_row_marks(summaryview, row);
4722 	}
4723 
4724 	debug_print("Message %d is set to move to %s\n",
4725 		    msginfo->msgnum, to_folder->path);
4726 }
4727 
summary_move_selected_to(SummaryView * summaryview,FolderItem * to_folder)4728 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4729 {
4730 	GList *cur;
4731 	GtkCMCTreeNode *sel_last = NULL;
4732 	gboolean froze = FALSE;
4733 
4734 	if (!to_folder) return;
4735 	if (!summaryview->folder_item ||
4736 	    FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4737 
4738 	if (summary_is_locked(summaryview)) return;
4739 
4740 	if (summaryview->folder_item == to_folder) {
4741 		alertpanel_error(_("Destination is same as current folder."));
4742 		return;
4743 	}
4744 
4745 	if (to_folder->no_select) {
4746 		alertpanel_error(_("The destination folder can only be used to "
4747 				   "store subfolders."));
4748 		return;
4749 	}
4750 
4751 	START_LONG_OPERATION(summaryview, FALSE);
4752 
4753 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4754 	     cur != NULL && cur->data != NULL; cur = cur->next) {
4755 		sel_last = GTK_CMCTREE_NODE(cur->data);
4756 		summary_move_row_to
4757 			(summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4758 	}
4759 	END_LONG_OPERATION(summaryview);
4760 
4761 	if (prefs_common.immediate_exec) {
4762 		summary_execute(summaryview);
4763 	} else {
4764 		GtkCMCTreeNode *node = NULL;
4765 		if (summaryview->sort_type == SORT_ASCENDING) {
4766 			node = summary_find_next_msg(summaryview, sel_last,TRUE);
4767 			if (!node || prefs_common.next_on_delete == FALSE)
4768 				node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4769 		} else {
4770 			node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4771 			if (!node || prefs_common.next_on_delete == FALSE)
4772 				node = summary_find_next_msg(summaryview, sel_last,TRUE);
4773 		}
4774 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4775 		summary_status_show(summaryview);
4776 	}
4777 
4778 	if (!summaryview->selected) { /* this was the last message */
4779 		GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree),
4780 							 GTK_CMCLIST(summaryview->ctree)->rows - 1);
4781 		if (node)
4782 			summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4783 	}
4784 
4785 }
4786 
summary_move_to(SummaryView * summaryview)4787 void summary_move_to(SummaryView *summaryview)
4788 {
4789 	FolderItem *to_folder;
4790 
4791 	if (!summaryview->folder_item ||
4792 	    FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4793 
4794 	to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE,
4795 			ngettext(
4796 				"Select folder to move selected message to",
4797 				"Select folder to move selected messages to",
4798 				g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4799 	);
4800 	summary_move_selected_to(summaryview, to_folder);
4801 }
4802 
summary_copy_row_to(SummaryView * summaryview,GtkCMCTreeNode * row,FolderItem * to_folder)4803 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4804 				FolderItem *to_folder)
4805 {
4806 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4807 	MsgInfo *msginfo;
4808 
4809 	cm_return_if_fail(to_folder != NULL);
4810 
4811 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4812 	cm_return_if_fail(msginfo);
4813 	procmsg_msginfo_set_to_folder(msginfo, to_folder);
4814 	if (MSG_IS_DELETED(msginfo->flags))
4815 		summaryview->deleted--;
4816 	if (MSG_IS_MOVE(msginfo->flags)) {
4817 		summaryview->moved--;
4818 	}
4819 
4820 	if (!MSG_IS_COPY(msginfo->flags)) {
4821 		summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED,
4822 			MSG_MOVE | MSG_MOVE_DONE);
4823 		summaryview->copied++;
4824 	} else {
4825 		summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
4826 	}
4827 	if (!prefs_common.immediate_exec) {
4828 		summary_set_row_marks(summaryview, row);
4829 	}
4830 
4831 	debug_print("Message %d is set to copy to %s\n",
4832 		    msginfo->msgnum, to_folder->path);
4833 }
4834 
summary_copy_selected_to(SummaryView * summaryview,FolderItem * to_folder)4835 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4836 {
4837 	GList *cur;
4838 	gboolean froze = FALSE;
4839 
4840 	if (!to_folder) return;
4841 	if (!summaryview->folder_item) return;
4842 
4843 	if (summary_is_locked(summaryview)) return;
4844 
4845 	if (summaryview->folder_item == to_folder) {
4846 		alertpanel_error
4847 			(_("Destination to copy is same as current folder."));
4848 		return;
4849 	}
4850 
4851 	if (to_folder->no_select) {
4852 		alertpanel_error(_("The destination folder can only be used to "
4853 				   "store subfolders."));
4854 		return;
4855 	}
4856 
4857 	START_LONG_OPERATION(summaryview, FALSE);
4858 
4859 	for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4860 	     cur != NULL && cur->data != NULL; cur = cur->next)
4861 		summary_copy_row_to
4862 			(summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4863 
4864 	END_LONG_OPERATION(summaryview);
4865 
4866 	if (prefs_common.immediate_exec)
4867 		summary_execute(summaryview);
4868 	else {
4869 		summary_status_show(summaryview);
4870 	}
4871 }
4872 
summary_copy_to(SummaryView * summaryview)4873 void summary_copy_to(SummaryView *summaryview)
4874 {
4875 	FolderItem *to_folder;
4876 
4877 	if (!summaryview->folder_item) return;
4878 
4879 	to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
4880 			ngettext(
4881 				"Select folder to copy selected message to",
4882 				"Select folder to copy selected messages to",
4883 				g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4884 	);
4885 	summary_copy_selected_to(summaryview, to_folder);
4886 }
4887 
summary_add_address(SummaryView * summaryview)4888 void summary_add_address(SummaryView *summaryview)
4889 {
4890 	MsgInfo *msginfo, *full_msginfo;
4891 	gchar *from;
4892 	GdkPixbuf *picture = NULL;
4893 	AvatarRender *avatarr;
4894 
4895 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4896 					      summaryview->selected);
4897 	if (!msginfo || !msginfo->from)
4898 		return;
4899 
4900 	Xstrdup_a(from, msginfo->from, return);
4901 	eliminate_address_comment(from);
4902 	extract_address(from);
4903 
4904 	full_msginfo = procmsg_msginfo_get_full_info(msginfo);
4905 
4906 	avatarr = avatars_avatarrender_new(full_msginfo);
4907 	hooks_invoke(AVATAR_IMAGE_RENDER_HOOKLIST, avatarr);
4908 
4909 	procmsg_msginfo_free(&full_msginfo);
4910 
4911 	if (avatarr->image)
4912 		picture = gtk_image_get_pixbuf(GTK_IMAGE(avatarr->image));
4913 
4914 #ifndef USE_ALT_ADDRBOOK
4915 	addressbook_add_contact(msginfo->fromname, from, NULL, picture);
4916 #else
4917 	if (addressadd_selection(msginfo->fromname, from, NULL, picture)) {
4918 		debug_print( "addressbook_add_contact - added\n" );
4919 	}
4920 #endif
4921 	avatars_avatarrender_free(avatarr);
4922 }
4923 
summary_select_all(SummaryView * summaryview)4924 void summary_select_all(SummaryView *summaryview)
4925 {
4926 	GtkCMCTreeNode *node;
4927 
4928 	if (!summaryview->folder_item) return;
4929 
4930 	if (GTK_CMCLIST(summaryview->ctree)->focus_row < 0) {
4931 		/* If no row is selected, select (but do not open) the first
4932 		 * row, to get summaryview into correct state for selecting all. */
4933 		debug_print("summary_select_all: no row selected, selecting first one\n");
4934 		if (GTK_CMCLIST(summaryview->ctree)->row_list != NULL) {
4935 			node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
4936 			summary_select_node(summaryview, node, FALSE);
4937 		}
4938 	}
4939 
4940 	/* Now select all rows while locking the summaryview for
4941 	 * faster performance. */
4942 	summary_lock(summaryview);
4943 	gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree));
4944 	summary_unlock(summaryview);
4945 
4946 	summary_status_show(summaryview);
4947 }
4948 
summary_unselect_all(SummaryView * summaryview)4949 void summary_unselect_all(SummaryView *summaryview)
4950 {
4951 	summary_lock(summaryview);
4952 	gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
4953 	summary_unlock(summaryview);
4954 	summary_status_show(summaryview);
4955 }
4956 
summary_select_thread(SummaryView * summaryview,gboolean delete_thread,gboolean trash_thread)4957 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread,
4958 			   gboolean trash_thread)
4959 {
4960 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4961 	GtkCMCTreeNode *node = NULL;
4962 	gboolean froze = FALSE;
4963 	GList *cur = NULL;
4964 	GList *copy = NULL;
4965 	if (!GTK_CMCLIST(summaryview->ctree)->selection)
4966 		return;
4967 
4968 
4969 	START_LONG_OPERATION(summaryview, FALSE);
4970 	copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection);
4971 	for (cur = copy; cur != NULL && cur->data != NULL;
4972 	     cur = cur->next) {
4973 		node = GTK_CMCTREE_NODE(cur->data);
4974 		if (!node)
4975 			continue;
4976 		while (GTK_CMCTREE_ROW(node)->parent != NULL)
4977 			node = GTK_CMCTREE_ROW(node)->parent;
4978 
4979 		gtk_cmctree_select_recursive(ctree, node);
4980 	}
4981 	g_list_free(copy);
4982 	END_LONG_OPERATION(summaryview);
4983 
4984 	if (trash_thread) {
4985 		if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
4986 			summary_delete(summaryview);
4987 		else
4988 			summary_delete_trash(summaryview);
4989 	} else if (delete_thread)
4990 		summary_delete(summaryview);
4991 
4992 	summary_status_show(summaryview);
4993 }
4994 
summary_save_as(SummaryView * summaryview)4995 void summary_save_as(SummaryView *summaryview)
4996 {
4997 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4998 	MsgInfo *msginfo;
4999 	gchar *filename = NULL;
5000 	gchar *src, *dest;
5001 	gchar *tmp;
5002 	gchar *filedir = NULL;
5003 
5004 	AlertValue aval = 0;
5005 
5006 	if (!summaryview->selected) return;
5007 	msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
5008 	if (!msginfo) return;
5009 
5010 	if (msginfo->subject) {
5011 		Xstrdup_a(filename, msginfo->subject, return);
5012 		subst_for_filename(filename);
5013 	}
5014 
5015 	manage_window_focus_in(summaryview->window, NULL, NULL);
5016 
5017 	if (filename && !g_utf8_validate(filename, -1, NULL)) {
5018 		gchar *converted_filename = conv_codeset_strdup(filename,
5019 					       conv_get_locale_charset_str(),
5020 					       CS_UTF_8);
5021 		if (!converted_filename) {
5022 			g_warning("summary_save_as(): failed to convert character set.");
5023 		} else {
5024 			g_free(filename);
5025 			filename = converted_filename;
5026 		}
5027 	}
5028 
5029 	dest = filename;
5030 	filename = NULL;
5031 	if (!dest) return;
5032 	if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir)
5033 		dest = g_strconcat(prefs_common.attach_save_dir, G_DIR_SEPARATOR_S,
5034 				   dest, NULL);
5035 	dest = filesel_select_file_save(_("Save as"), dest);
5036 	if (!dest) return;
5037 
5038 	if (is_file_exist(dest)) {
5039 		aval = alertpanel(_("Append or Overwrite"),
5040 				  _("Append or overwrite existing file?"),
5041 				  _("_Append"), _("_Overwrite"), GTK_STOCK_CANCEL,
5042 					ALERTFOCUS_FIRST);
5043 		if (aval != 0 && aval != 1)
5044 			return;
5045 	}
5046 
5047 	src = procmsg_get_message_file(msginfo);
5048 	tmp = g_path_get_basename(dest);
5049 
5050 	if ( aval==0 ) { /* append */
5051 		if (append_file(src, dest, TRUE) < 0)
5052 			alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5053 	} else { /* overwrite */
5054 		if (copy_file(src, dest, TRUE) < 0)
5055 			alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5056 	}
5057 	g_free(src);
5058 
5059 	/*
5060 	 * If two or more msgs are selected,
5061 	 * append them to the output file.
5062 	 */
5063 	if (GTK_CMCLIST(ctree)->selection->next) {
5064 		GList *item;
5065 		for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) {
5066 			msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data));
5067 			if (!msginfo) break;
5068 			src = procmsg_get_message_file(msginfo);
5069 			if (append_file(src, dest, TRUE) < 0) {
5070 				alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5071 			}
5072 			g_free(src);
5073 		}
5074 	}
5075 
5076 	filedir = g_path_get_dirname(dest);
5077 	if (filedir && strcmp(filedir, ".")) {
5078 		g_free(prefs_common.attach_save_dir);
5079 		prefs_common.attach_save_dir = g_filename_to_utf8(filedir, -1, NULL, NULL, NULL);
5080 	}
5081 
5082 	g_free(dest);
5083 	g_free(tmp);
5084 }
5085 
summary_print(SummaryView * summaryview)5086 void summary_print(SummaryView *summaryview)
5087 {
5088 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5089 	GList *cur;
5090 	gchar *msg = g_strdup_printf(_("You are about to print %d "
5091 				       "messages, one by one. Do you "
5092 				       "want to continue?"),
5093 				       g_list_length(clist->selection));
5094 	if (g_list_length(clist->selection) > 9
5095 	&&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_YES,
5096 		NULL, ALERTFOCUS_SECOND) != G_ALERTALTERNATE) {
5097 		g_free(msg);
5098 		return;
5099 	}
5100 	g_free(msg);
5101 
5102 	if (clist->selection == NULL) return;
5103 	for (cur = clist->selection;
5104 	     cur != NULL && cur->data != NULL;
5105 	     cur = cur->next) {
5106 		GtkCMCTreeNode *node = GTK_CMCTREE_NODE(cur->data);
5107 		MsgInfo *msginfo = gtk_cmctree_node_get_row_data(
5108 					GTK_CMCTREE(summaryview->ctree),
5109 					node);
5110 		gint sel_start = -1, sel_end = -1, partnum = 0;
5111 
5112 		if (node == summaryview->displayed) {
5113 			partnum = mimeview_get_selected_part_num(summaryview->messageview->mimeview);
5114 			textview_get_selection_offsets(summaryview->messageview->mimeview->textview,
5115 				&sel_start, &sel_end);
5116 		}
5117 		messageview_print(msginfo, summaryview->messageview->all_headers,
5118 			sel_start, sel_end, partnum);
5119 	}
5120 }
5121 
summary_execute(SummaryView * summaryview)5122 gboolean summary_execute(SummaryView *summaryview)
5123 {
5124 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5125 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5126 	GtkCMCTreeNode *node, *next;
5127 	GtkCMCTreeNode *new_selected = NULL;
5128 	gint move_val = -1;
5129 
5130 	if (!summaryview->folder_item) return FALSE;
5131 
5132 	if (summary_is_locked(summaryview)) return FALSE;
5133 	summary_lock(summaryview);
5134 
5135 	summary_freeze(summaryview);
5136 
5137 	main_window_cursor_wait(summaryview->mainwin);
5138 
5139 	if (summaryview->threaded)
5140 		summary_unthread_for_exec(summaryview);
5141 
5142 	folder_item_update_freeze();
5143 	move_val = summary_execute_move(summaryview);
5144 	summary_execute_copy(summaryview);
5145 	summary_execute_delete(summaryview);
5146 
5147 	node = GTK_CMCTREE_NODE(clist->row_list);
5148 	for (; node != NULL; node = next) {
5149 		next = gtkut_ctree_node_next(ctree, node);
5150 		if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5151 
5152 		if (node == summaryview->displayed) {
5153 			messageview_clear(summaryview->messageview);
5154 			summary_cancel_mark_read_timeout(summaryview);
5155 			summaryview->displayed = NULL;
5156 		}
5157 		if (GTK_CMCTREE_ROW(node)->children != NULL) {
5158 			next = NULL;
5159 			if (GTK_CMCTREE_ROW(node)->sibling) {
5160 				next = GTK_CMCTREE_ROW(node)->sibling;
5161 			} else {
5162 				GtkCMCTreeNode *parent = NULL;
5163 				for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5164 				     parent = GTK_CMCTREE_ROW(parent)->parent) {
5165 					if (GTK_CMCTREE_ROW(parent)->sibling) {
5166 						next = GTK_CMCTREE_ROW(parent)->sibling;
5167 					}
5168 				}
5169 			}
5170 		}
5171 
5172 		if (!new_selected &&
5173 		    gtkut_ctree_node_is_selected(ctree, node)) {
5174 			summary_unselect_all(summaryview);
5175 			if (summaryview->sort_type == SORT_ASCENDING) {
5176 				new_selected = summary_find_next_msg(summaryview, node,TRUE);
5177 				if (!new_selected || prefs_common.next_on_delete == FALSE)
5178 					new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5179 			} else {
5180 				new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5181 				if (!new_selected || prefs_common.next_on_delete == FALSE)
5182 					new_selected = summary_find_next_msg(summaryview, node,TRUE);
5183 			}
5184 		}
5185 
5186 		gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5187 	}
5188 
5189 	folder_item_update_thaw();
5190 
5191 	if (new_selected) {
5192 		summary_unlock(summaryview);
5193 		summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5194 		summary_lock(summaryview);
5195 	}
5196 
5197 	if (summaryview->threaded) {
5198 		summary_thread_build(summaryview);
5199 		summary_thread_init(summaryview);
5200 	}
5201 
5202 	summary_thaw(summaryview);
5203 
5204 	summaryview->selected = clist->selection ?
5205 		GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5206 
5207 	if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5208 		menu_set_insensitive_all
5209 			(GTK_MENU_SHELL(summaryview->popupmenu));
5210 		folderview_grab_focus(summaryview->folderview);
5211 	} else {
5212 		menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5213 		gtk_widget_grab_focus(summaryview->ctree);
5214 	}
5215 	summary_update_status(summaryview);
5216 	summary_status_show(summaryview);
5217 
5218 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5219 
5220 	summary_unlock(summaryview);
5221 
5222 	main_window_cursor_normal(summaryview->mainwin);
5223 
5224 	if (move_val < 0)
5225 		summary_show(summaryview, summaryview->folder_item, FALSE);
5226 	return TRUE;
5227 }
5228 
summary_expunge(SummaryView * summaryview)5229 gboolean summary_expunge(SummaryView *summaryview)
5230 {
5231 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5232 	GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5233 	GtkCMCTreeNode *node, *next;
5234 	GtkCMCTreeNode *new_selected = NULL;
5235 
5236 	if (!summaryview->folder_item) return FALSE;
5237 
5238 	if (summary_is_locked(summaryview)) return FALSE;
5239 
5240 	summary_lock(summaryview);
5241 
5242 	summary_freeze(summaryview);
5243 
5244 	main_window_cursor_wait(summaryview->mainwin);
5245 
5246 	if (summaryview->threaded)
5247 		summary_unthread_for_exec(summaryview);
5248 
5249 	folder_item_update_freeze();
5250 	summary_execute_expunge(summaryview);
5251 
5252 	node = GTK_CMCTREE_NODE(clist->row_list);
5253 	for (; node != NULL; node = next) {
5254 		next = gtkut_ctree_node_next(ctree, node);
5255 		if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5256 
5257 		if (node == summaryview->displayed) {
5258 			messageview_clear(summaryview->messageview);
5259 			summary_cancel_mark_read_timeout(summaryview);
5260 			summaryview->displayed = NULL;
5261 		}
5262 		if (GTK_CMCTREE_ROW(node)->children != NULL) {
5263 			next = NULL;
5264 			if (GTK_CMCTREE_ROW(node)->sibling) {
5265 				next = GTK_CMCTREE_ROW(node)->sibling;
5266 			} else {
5267 				GtkCMCTreeNode *parent = NULL;
5268 				for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5269 				     parent = GTK_CMCTREE_ROW(parent)->parent) {
5270 					if (GTK_CMCTREE_ROW(parent)->sibling) {
5271 						next = GTK_CMCTREE_ROW(parent)->sibling;
5272 					}
5273 				}
5274 			}
5275 		}
5276 
5277 		if (!new_selected &&
5278 		    gtkut_ctree_node_is_selected(ctree, node)) {
5279 			summary_unselect_all(summaryview);
5280 			new_selected = summary_find_next_msg(summaryview, node,TRUE);
5281 			if (!new_selected)
5282 				new_selected = summary_find_prev_msg
5283 					(summaryview, node,TRUE);
5284 		}
5285 
5286 		gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5287 	}
5288 
5289 	folder_item_update_thaw();
5290 
5291 	if (new_selected) {
5292 		summary_unlock(summaryview);
5293 		summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5294 		summary_lock(summaryview);
5295 	}
5296 
5297 	if (summaryview->threaded) {
5298 		summary_thread_build(summaryview);
5299 		summary_thread_init(summaryview);
5300 	}
5301 
5302 	summary_thaw(summaryview);
5303 
5304 	summaryview->selected = clist->selection ?
5305 		GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5306 
5307 	if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5308 		menu_set_insensitive_all
5309 			(GTK_MENU_SHELL(summaryview->popupmenu));
5310 		folderview_grab_focus(summaryview->folderview);
5311 	} else {
5312 		menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5313 		gtk_widget_grab_focus(summaryview->ctree);
5314 	}
5315 
5316 	summary_update_status(summaryview);
5317 	summary_status_show(summaryview);
5318 
5319 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5320 
5321 	summary_unlock(summaryview);
5322 
5323 	main_window_cursor_normal(summaryview->mainwin);
5324 
5325 	return TRUE;
5326 }
5327 
summary_execute_move(SummaryView * summaryview)5328 static gint summary_execute_move(SummaryView *summaryview)
5329 {
5330 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5331 	GSList *cur;
5332 	gint val = -1;
5333 	/* search moving messages and execute */
5334 
5335 	gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func,
5336 			summaryview);
5337 
5338 	if (summaryview->mlist) {
5339 		hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5340 			summaryview->msginfo_update_callback_id);
5341 		val = procmsg_move_messages(summaryview->mlist);
5342 		summaryview->msginfo_update_callback_id =
5343 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5344 			summary_update_msg, (gpointer) summaryview);
5345 
5346 		for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5347 			procmsg_msginfo_free((MsgInfo **)&(cur->data));
5348 		}
5349 
5350 		g_slist_free(summaryview->mlist);
5351 		summaryview->mlist = NULL;
5352 		return val;
5353 	}
5354 	return 0;
5355 }
5356 
summary_execute_move_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)5357 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5358 				      gpointer data)
5359 {
5360 	SummaryView *summaryview = data;
5361 	MsgInfo *msginfo;
5362 
5363 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5364 
5365 	if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
5366 		summaryview->mlist =
5367 			g_slist_prepend(summaryview->mlist, msginfo);
5368 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
5369 
5370 		if (msginfo->msgid && *msginfo->msgid &&
5371 		    node == g_hash_table_lookup(summaryview->msgid_table,
5372 						msginfo->msgid))
5373 			g_hash_table_remove(summaryview->msgid_table,
5374 					    msginfo->msgid);
5375 		if (prefs_common.thread_by_subject &&
5376 		    msginfo->subject && *msginfo->subject &&
5377 		    node == subject_table_lookup(summaryview->subject_table,
5378 						 msginfo->subject)) {
5379 			subject_table_remove(summaryview->subject_table,
5380 					     msginfo->subject);
5381 		}
5382 	}
5383 }
5384 
summary_execute_copy(SummaryView * summaryview)5385 static void summary_execute_copy(SummaryView *summaryview)
5386 {
5387 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5388 
5389 	/* search copying messages and execute */
5390 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5391 		summaryview->msginfo_update_callback_id);
5392 	gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
5393 				summaryview);
5394 
5395 	if (summaryview->mlist) {
5396 		summaryview->mlist = g_slist_reverse(summaryview->mlist);
5397 		procmsg_copy_messages(summaryview->mlist);
5398 
5399 		g_slist_free(summaryview->mlist);
5400 		summaryview->mlist = NULL;
5401 	}
5402 	summaryview->msginfo_update_callback_id =
5403 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5404 			summary_update_msg, (gpointer) summaryview);
5405 }
5406 
summary_execute_copy_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)5407 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5408 				      gpointer data)
5409 {
5410 	SummaryView *summaryview = data;
5411 	MsgInfo *msginfo;
5412 
5413 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5414 
5415 	if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
5416 		summaryview->mlist =
5417 			g_slist_prepend(summaryview->mlist, msginfo);
5418 
5419 		summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
5420 		summary_set_row_marks(summaryview, node);
5421 	}
5422 }
5423 
summary_execute_delete(SummaryView * summaryview)5424 static void summary_execute_delete(SummaryView *summaryview)
5425 {
5426 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5427 	GSList *cur;
5428 
5429 	/* search deleting messages and execute */
5430 	gtk_cmctree_pre_recursive
5431 		(ctree, NULL, summary_execute_delete_func, summaryview);
5432 
5433 	if (!summaryview->mlist) return;
5434 
5435 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5436 		summaryview->msginfo_update_callback_id);
5437 
5438 	folder_item_remove_msgs(summaryview->folder_item,
5439 				summaryview->mlist);
5440 
5441 	summaryview->msginfo_update_callback_id =
5442 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5443 			summary_update_msg, (gpointer) summaryview);
5444 
5445 	for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5446 			procmsg_msginfo_free((MsgInfo **)&(cur->data));
5447 	}
5448 
5449 	g_slist_free(summaryview->mlist);
5450 	summaryview->mlist = NULL;
5451 }
5452 
summary_execute_delete_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)5453 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5454 					gpointer data)
5455 {
5456 	SummaryView *summaryview = data;
5457 	MsgInfo *msginfo;
5458 
5459 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5460 
5461 	if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5462 		summaryview->mlist =
5463 			g_slist_prepend(summaryview->mlist, msginfo);
5464 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
5465 
5466 		if (msginfo->msgid && *msginfo->msgid &&
5467 		    node == g_hash_table_lookup(summaryview->msgid_table,
5468 						msginfo->msgid)) {
5469 			g_hash_table_remove(summaryview->msgid_table,
5470 					    msginfo->msgid);
5471 		}
5472 		if (prefs_common.thread_by_subject &&
5473 		    msginfo->subject && *msginfo->subject &&
5474 		    node == subject_table_lookup(summaryview->subject_table,
5475 						 msginfo->subject)) {
5476 			subject_table_remove(summaryview->subject_table,
5477 					     msginfo->subject);
5478 		}
5479 	}
5480 }
5481 
summary_execute_expunge_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)5482 static void summary_execute_expunge_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5483 					gpointer data)
5484 {
5485 	SummaryView *summaryview = data;
5486 	MsgInfo *msginfo;
5487 
5488 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5489 
5490 	if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5491 		summaryview->mlist =
5492 			g_slist_prepend(summaryview->mlist, msginfo);
5493 		gtk_cmctree_node_set_row_data(ctree, node, NULL);
5494 
5495 		if (msginfo->msgid && *msginfo->msgid &&
5496 		    node == g_hash_table_lookup(summaryview->msgid_table,
5497 						msginfo->msgid)) {
5498 			g_hash_table_remove(summaryview->msgid_table,
5499 					    msginfo->msgid);
5500 		}
5501 		if (prefs_common.thread_by_subject &&
5502 		    msginfo->subject && *msginfo->subject &&
5503 		    node == subject_table_lookup(summaryview->subject_table,
5504 						 msginfo->subject)) {
5505 			subject_table_remove(summaryview->subject_table,
5506 					     msginfo->subject);
5507 		}
5508 	}
5509 }
5510 
summary_execute_expunge(SummaryView * summaryview)5511 static void summary_execute_expunge(SummaryView *summaryview)
5512 {
5513 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5514 	GSList *cur;
5515 
5516 	gtk_cmctree_pre_recursive
5517 		(ctree, NULL, summary_execute_expunge_func, summaryview);
5518 
5519 	hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5520 		summaryview->msginfo_update_callback_id);
5521 
5522 	folder_item_expunge(summaryview->folder_item);
5523 
5524 	summaryview->msginfo_update_callback_id =
5525 		hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5526 			summary_update_msg, (gpointer) summaryview);
5527 	for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
5528 		procmsg_msginfo_free((MsgInfo **)&(cur->data));
5529 
5530 	g_slist_free(summaryview->mlist);
5531 	summaryview->mlist = NULL;
5532 }
5533 
5534 /* thread functions */
5535 
summary_thread_build(SummaryView * summaryview)5536 static void summary_thread_build(SummaryView *summaryview)
5537 {
5538 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5539 	GtkCMCTreeNode *node;
5540 	GtkCMCTreeNode *next;
5541 	GtkCMCTreeNode *parent;
5542 	MsgInfo *msginfo;
5543         GSList *reflist;
5544 
5545 	summary_lock(summaryview);
5546 
5547 	debug_print("Building threads...\n");
5548 	STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
5549 	main_window_cursor_wait(summaryview->mainwin);
5550 
5551 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
5552 				       G_CALLBACK(summary_tree_expanded), summaryview);
5553 	summary_freeze(summaryview);
5554 
5555 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5556 	while (node) {
5557 		next = GTK_CMCTREE_ROW(node)->sibling;
5558 
5559 		msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5560 
5561 		parent = NULL;
5562 
5563 		if (msginfo && msginfo->inreplyto) {
5564 			parent = g_hash_table_lookup(summaryview->msgid_table,
5565 						     msginfo->inreplyto);
5566 
5567 			if (!parent && msginfo->references) {
5568 				for (reflist = msginfo->references;
5569 				     reflist != NULL; reflist = reflist->next)
5570 					if ((parent = g_hash_table_lookup
5571 						(summaryview->msgid_table,
5572 						 reflist->data)))
5573 						break;
5574 			}
5575 		}
5576 
5577 		if (msginfo && prefs_common.thread_by_subject && parent == NULL) {
5578 			parent = subject_table_lookup
5579 				(summaryview->subject_table,
5580 				 msginfo->subject);
5581 		}
5582 
5583 		if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) {
5584 			gtk_cmctree_move(ctree, node, parent, NULL);
5585 		}
5586 
5587 		node = next;
5588 	}
5589 
5590 	gtkut_ctree_set_focus_row(ctree, summaryview->selected);
5591 
5592 	summary_thaw(summaryview);
5593 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5594 					 G_CALLBACK(summary_tree_expanded), summaryview);
5595 
5596 	debug_print("Building threads done.\n");
5597 	STATUSBAR_POP(summaryview->mainwin);
5598 	main_window_cursor_normal(summaryview->mainwin);
5599 
5600 	summaryview->threaded = TRUE;
5601 
5602 	summary_unlock(summaryview);
5603 }
5604 
summary_thread_init(SummaryView * summaryview)5605 static void summary_thread_init(SummaryView *summaryview)
5606 {
5607 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5608 	GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5609 	GtkCMCTreeNode *next;
5610 	START_TIMING("");
5611 	if (!summaryview->thread_collapsed) {
5612 		g_signal_handlers_block_by_func(G_OBJECT(ctree),
5613 				       G_CALLBACK(summary_tree_expanded), summaryview);
5614 		while (node) {
5615 			next = GTK_CMCTREE_ROW(node)->sibling;
5616 			if (GTK_CMCTREE_ROW(node)->children)
5617 				gtk_cmctree_expand_recursive(ctree, node);
5618 			node = next;
5619 		}
5620 		g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5621 				       G_CALLBACK(summary_tree_expanded), summaryview);
5622 	}
5623 	END_TIMING();
5624 }
5625 
summary_unthread_for_exec(SummaryView * summaryview)5626 static void summary_unthread_for_exec(SummaryView *summaryview)
5627 {
5628 	GtkCMCTreeNode *node;
5629 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5630 	gboolean froze = FALSE;
5631 
5632 	debug_print("Unthreading for execution...\n");
5633 
5634 	START_LONG_OPERATION(summaryview, TRUE);
5635 	for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5636 	     node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) {
5637 		summary_unthread_for_exec_func(ctree, node, summaryview);
5638 	}
5639 
5640 	END_LONG_OPERATION(summaryview);
5641 
5642 	debug_print("Unthreading for execution done.\n");
5643 }
5644 
summary_unthread_for_exec_func(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)5645 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5646 					   gpointer data)
5647 {
5648 	MsgInfo *msginfo;
5649 	GtkCMCTreeNode *top_parent;
5650 	GtkCMCTreeNode *child;
5651 	GtkCMCTreeNode *sibling;
5652 	SummaryView * summaryview = (SummaryView *)data;
5653 	msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5654 
5655 	if (!msginfo ||
5656 	    (!MSG_IS_MOVE(msginfo->flags) &&
5657 	     !MSG_IS_DELETED(msginfo->flags)))
5658 		return;
5659 	child = GTK_CMCTREE_ROW(node)->children;
5660 	if (!child) return;
5661 
5662 	if (node == summaryview->selected)
5663 		summaryview->selected = NULL;
5664 	if (node == summaryview->displayed)
5665 		summaryview->displayed = NULL;
5666 
5667 	summary_cancel_mark_read_timeout(summaryview);
5668 
5669 	for (top_parent = node;
5670 	     GTK_CMCTREE_ROW(top_parent)->parent != NULL;
5671 	     top_parent = GTK_CMCTREE_ROW(top_parent)->parent)
5672 		;
5673 	sibling = GTK_CMCTREE_ROW(top_parent)->sibling;
5674 
5675 	GTK_SCTREE(ctree)->sorting = TRUE;
5676 	while (child != NULL) {
5677 		GtkCMCTreeNode *next_child;
5678 		MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child);
5679 
5680 		next_child = GTK_CMCTREE_ROW(child)->sibling;
5681 
5682 		if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) {
5683 			gtk_cmctree_move(ctree, child,
5684 				NULL,
5685 				sibling);
5686 		} else {
5687 			if (child == summaryview->displayed) {
5688 				messageview_clear(summaryview->messageview);
5689 				summaryview->displayed = NULL;
5690 			}
5691 			if (child == summaryview->selected) {
5692 				messageview_clear(summaryview->messageview);
5693 				summaryview->selected = NULL;
5694 			}
5695 		}
5696 		child = next_child;
5697 	}
5698 	GTK_SCTREE(ctree)->sorting = FALSE;
5699 }
5700 
summary_expand_threads(SummaryView * summaryview)5701 void summary_expand_threads(SummaryView *summaryview)
5702 {
5703 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5704 	GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5705 	GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5706 
5707 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
5708 				       G_CALLBACK(summary_tree_expanded), summaryview);
5709 	summary_freeze(summaryview);
5710 	GTK_SCTREE(ctree)->sorting = TRUE;
5711 
5712 	while (node) {
5713 		if (GTK_CMCTREE_ROW(node)->children) {
5714 			gtk_cmctree_expand(ctree, node);
5715 			summary_set_row_marks(summaryview, node);
5716 		}
5717 		node = GTK_CMCTREE_NODE_NEXT(node);
5718 	}
5719 
5720 	GTK_SCTREE(ctree)->sorting = FALSE;
5721 	if (focus_node) {
5722 		GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5723 	}
5724 	summary_thaw(summaryview);
5725 
5726 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5727 					 G_CALLBACK(summary_tree_expanded), summaryview);
5728 
5729 	summaryview->thread_collapsed = FALSE;
5730 
5731 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5732 }
5733 
summary_collapse_threads(SummaryView * summaryview)5734 void summary_collapse_threads(SummaryView *summaryview)
5735 {
5736 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5737 	GtkCMCTreeNode *node = NULL;
5738 	GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5739 
5740 	g_signal_handlers_block_by_func(G_OBJECT(ctree),
5741 				       G_CALLBACK(summary_tree_collapsed), summaryview);
5742 	summary_freeze(summaryview);
5743 	GTK_SCTREE(ctree)->sorting = TRUE;
5744 
5745 	node = focus_node;
5746 	while (node && GTK_CMCTREE_ROW(node)->parent) {
5747 		focus_node = node = GTK_CMCTREE_ROW(node)->parent;
5748 	}
5749 	summary_select_node(summaryview, node, OPEN_SELECTED_ON_DIRECTIONAL);
5750 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5751 	while (node) {
5752 		if (GTK_CMCTREE_ROW(node)->children) {
5753 			gtk_cmctree_collapse(ctree, node);
5754 			summary_set_row_marks(summaryview, node);
5755 		}
5756 		node = GTK_CMCTREE_ROW(node)->sibling;
5757 	}
5758 
5759 	GTK_SCTREE(ctree)->sorting = FALSE;
5760 	if (focus_node) {
5761 		GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5762 	}
5763 	GTK_SCTREE(ctree)->anchor_row =
5764 			gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row);
5765 	summary_thaw(summaryview);
5766 	g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5767 					 G_CALLBACK(summary_tree_collapsed), summaryview);
5768 
5769 	summaryview->thread_collapsed = TRUE;
5770 
5771 	gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5772 }
5773 
account_rules_radio_button_toggled_cb(GtkToggleButton * btn,gpointer data)5774 static void account_rules_radio_button_toggled_cb(GtkToggleButton *btn, gpointer data)
5775 {
5776 	prefs_common.apply_per_account_filtering_rules = GPOINTER_TO_INT(data);
5777 }
5778 
summary_filter_get_mode(void)5779 static gboolean summary_filter_get_mode(void)
5780 /* ask what to do w/ them: skip them, apply them regardless to the account,
5781    use the current account */
5782 {
5783 	/* TODO: eventually also propose to use the current folder's default account,
5784 	   if it is set */
5785 	/* TODO: eventually allow to select the account to use from a optmenu */
5786 
5787 	GtkWidget *vbox;
5788 	GtkWidget *account_rules_skip;
5789 	GtkWidget *account_rules_force;
5790 	GtkWidget *account_rules_user_current;
5791 	AlertValue val;
5792 
5793 	vbox = gtk_vbox_new (FALSE, 0);
5794 
5795 	account_rules_skip = gtk_radio_button_new_with_label
5796 							(NULL, _("Skip these rules"));
5797 	account_rules_force = gtk_radio_button_new_with_label_from_widget
5798 							(GTK_RADIO_BUTTON(account_rules_skip),
5799 							_("Apply these rules regardless of the account they belong to"));
5800 	account_rules_user_current = gtk_radio_button_new_with_label_from_widget
5801 							(GTK_RADIO_BUTTON(account_rules_skip),
5802 							_("Apply these rules if they apply to the current account"));
5803 	gtk_box_pack_start (GTK_BOX (vbox), account_rules_skip, FALSE, FALSE, 0);
5804 	gtk_box_pack_start (GTK_BOX (vbox), account_rules_force, FALSE, FALSE, 0);
5805 	gtk_box_pack_start (GTK_BOX (vbox), account_rules_user_current, FALSE, FALSE, 0);
5806 	g_signal_connect(G_OBJECT(account_rules_skip), "toggled",
5807 			G_CALLBACK(account_rules_radio_button_toggled_cb),
5808 			GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_SKIP));
5809 	g_signal_connect(G_OBJECT(account_rules_force), "toggled",
5810 			G_CALLBACK(account_rules_radio_button_toggled_cb),
5811 			GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_FORCE));
5812 	g_signal_connect(G_OBJECT(account_rules_user_current), "toggled",
5813 			G_CALLBACK(account_rules_radio_button_toggled_cb),
5814 			GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_USE_CURRENT));
5815 	switch (prefs_common.apply_per_account_filtering_rules) {
5816 	case FILTERING_ACCOUNT_RULES_SKIP:
5817 		gtk_toggle_button_set_active(
5818 				GTK_TOGGLE_BUTTON(account_rules_skip), TRUE);
5819 		break;
5820 	case FILTERING_ACCOUNT_RULES_FORCE:
5821 		gtk_toggle_button_set_active(
5822 				GTK_TOGGLE_BUTTON(account_rules_force), TRUE);
5823 		break;
5824 	case FILTERING_ACCOUNT_RULES_USE_CURRENT:
5825 		gtk_toggle_button_set_active(
5826 				GTK_TOGGLE_BUTTON(account_rules_user_current), TRUE);
5827 		break;
5828 	}
5829 
5830 	val = alertpanel_with_widget(
5831 			_("Filtering"),
5832 			_("There are some filtering rules that belong to an account.\n"
5833 			  "Please choose what to do with these rules:"),
5834 			GTK_STOCK_CANCEL, _("_Filter"), NULL, ALERTFOCUS_SECOND,
5835 			TRUE, vbox);
5836 
5837 	if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) {
5838 		return FALSE;
5839 	} else if (val & G_ALERTDISABLE)
5840 		prefs_common.ask_apply_per_account_filtering_rules = FALSE;
5841 
5842 	return TRUE;
5843 }
5844 
summary_filter(SummaryView * summaryview,gboolean selected_only)5845 void summary_filter(SummaryView *summaryview, gboolean selected_only)
5846 {
5847 	GSList *mlist = NULL, *cur_list;
5848 	PrefsAccount *ac_prefs = NULL;
5849 	summary_lock(summaryview);
5850 
5851 	/* are there any per-account filtering rules? */
5852 	if (prefs_common.ask_apply_per_account_filtering_rules == TRUE &&
5853 		filtering_peek_per_account_rules(filtering_rules)) {
5854 
5855 		if (summary_filter_get_mode() == FALSE) {
5856 			summary_unlock(summaryview);
5857 			return;
5858 		}
5859 	}
5860 
5861 	folder_item_update_freeze();
5862 
5863 	debug_print("filtering...\n");
5864 	STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
5865 	main_window_cursor_wait(summaryview->mainwin);
5866 
5867 	summary_freeze(summaryview);
5868 
5869 	if (selected_only) {
5870 		GList *cur;
5871 
5872 		for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
5873 	     	     cur != NULL && cur->data != NULL; cur = cur->next) {
5874 			mlist = g_slist_prepend(mlist,
5875 				 procmsg_msginfo_new_ref(
5876 				  GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data)));
5877 		}
5878 		mlist = g_slist_reverse(mlist);
5879 	} else {
5880 		mlist = folder_item_get_msg_list(summaryview->folder_item);
5881 	}
5882 
5883 	ac_prefs = ((summaryview->folder_item->folder != NULL) &&
5884 			(summaryview->folder_item->folder->account != NULL))
5885 		? summaryview->folder_item->folder->account : NULL;
5886 
5887 	folder_item_set_batch(summaryview->folder_item, TRUE);
5888 	for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5889 		summary_filter_func((MsgInfo *)cur_list->data, ac_prefs);
5890 	}
5891 	folder_item_set_batch(summaryview->folder_item, FALSE);
5892 
5893 	filtering_move_and_copy_msgs(mlist);
5894 
5895 	for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5896 		procmsg_msginfo_free((MsgInfo **)&(cur_list->data));
5897 	}
5898 	g_slist_free(mlist);
5899 
5900 	summary_thaw(summaryview);
5901 
5902 	folder_item_update_thaw();
5903 	debug_print("filtering done.\n");
5904 	STATUSBAR_POP(summaryview->mainwin);
5905 	main_window_cursor_normal(summaryview->mainwin);
5906 
5907 	summary_unlock(summaryview);
5908 
5909 	/*
5910 	 * CLAWS: summary_show() only valid after having a lock. ideally
5911 	 * we want the lock to be context aware...
5912 	 */
5913 	summary_show(summaryview, summaryview->folder_item, TRUE);
5914 }
5915 
summary_filter_func(MsgInfo * msginfo,PrefsAccount * ac_prefs)5916 static void summary_filter_func(MsgInfo *msginfo, PrefsAccount *ac_prefs)
5917 {
5918 	MailFilteringData mail_filtering_data;
5919 
5920 	mail_filtering_data.msginfo = msginfo;
5921 	mail_filtering_data.msglist = NULL;
5922 	mail_filtering_data.filtered = NULL;
5923 	mail_filtering_data.unfiltered = NULL;
5924 	if (hooks_invoke(MAIL_MANUAL_FILTERING_HOOKLIST, &mail_filtering_data))
5925 		return;
5926 
5927 	filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
5928 			FILTERING_MANUALLY, NULL);
5929 }
5930 
summary_msginfo_filter_open(FolderItem * item,MsgInfo * msginfo,PrefsFilterType type,gint processing_rule)5931 void summary_msginfo_filter_open(FolderItem * item, MsgInfo *msginfo,
5932 				 PrefsFilterType type, gint processing_rule)
5933 {
5934 	gchar *header = NULL;
5935 	gchar *key = NULL;
5936 
5937 	procmsg_get_filter_keyword(msginfo, &header, &key, type);
5938 
5939 	if (processing_rule) {
5940 		if (item == NULL)
5941 			prefs_filtering_open(&pre_global_processing,
5942 					     _("Processing rules to apply before folder rules"),
5943 					     MANUAL_ANCHOR_PROCESSING,
5944 					     header, key, FALSE);
5945 		else
5946 			prefs_filtering_open(&item->prefs->processing,
5947 					     _("Processing configuration"),
5948 					     MANUAL_ANCHOR_PROCESSING,
5949 					     header, key, FALSE);
5950 	}
5951 	else {
5952 		prefs_filtering_open(&filtering_rules,
5953 				_("Filtering configuration"),
5954 				MANUAL_ANCHOR_FILTERING,
5955 				header, key, TRUE);
5956 	}
5957 
5958 	g_free(header);
5959 	g_free(key);
5960 }
5961 
summary_filter_open(SummaryView * summaryview,PrefsFilterType type,gint processing_rule)5962 void summary_filter_open(SummaryView *summaryview, PrefsFilterType type,
5963 			 gint processing_rule)
5964 {
5965 	MsgInfo *msginfo;
5966 	FolderItem * item;
5967 
5968 	if (!summaryview->selected) return;
5969 
5970 	msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
5971 					      summaryview->selected);
5972 	if (!msginfo) return;
5973 
5974 	item = summaryview->folder_item;
5975 	summary_msginfo_filter_open(item, msginfo, type, processing_rule);
5976 }
5977 
5978 /* color label */
5979 
5980 #define N_COLOR_LABELS colorlabel_get_color_count()
5981 
summary_colorlabel_menu_item_activate_cb(GtkWidget * widget,gpointer data)5982 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
5983 						     gpointer data)
5984 {
5985 	guint color = GPOINTER_TO_UINT(data);
5986 	SummaryView *summaryview;
5987 
5988 	summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5989 	cm_return_if_fail(summaryview != NULL);
5990 
5991 	/* "dont_toggle" state set? */
5992 	if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
5993 				"dont_toggle"))
5994 		return;
5995 
5996 	summary_set_colorlabel(summaryview, color, NULL);
5997 }
5998 
5999 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
6000  * for the message; not the color index */
summary_set_colorlabel_color(GtkCMCTree * ctree,GtkCMCTreeNode * node,guint labelcolor)6001 void summary_set_colorlabel_color(GtkCMCTree *ctree, GtkCMCTreeNode *node,
6002 				  guint labelcolor)
6003 {
6004 	GdkColor color;
6005 	GtkStyle *style, *prev_style, *ctree_style;
6006 	MsgInfo *msginfo;
6007 	gint color_index;
6008 
6009 	msginfo = gtk_cmctree_node_get_row_data(ctree, node);
6010 	cm_return_if_fail(msginfo);
6011 
6012 	color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
6013 	ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
6014 	prev_style = gtk_cmctree_node_get_row_style(ctree, node);
6015 
6016 	if (color_index < 0 || color_index >= N_COLOR_LABELS) {
6017 		if (!prev_style) return;
6018 		style = gtk_style_copy(prev_style);
6019 		color = ctree_style->text[GTK_STATE_NORMAL];
6020 		style->text[GTK_STATE_NORMAL] = color;
6021 		color = ctree_style->text[GTK_STATE_SELECTED];
6022 		style->text[GTK_STATE_SELECTED] = color;
6023 	} else {
6024 		if (prev_style)
6025 			style = gtk_style_copy(prev_style);
6026 		else
6027 			style = gtk_style_copy(ctree_style);
6028 		color = colorlabel_get_color(color_index);
6029 		style->text[GTK_STATE_NORMAL] = color;
6030 		/* get the average of label color and selected fg color
6031 		   for visibility */
6032 		style->text[GTK_STATE_SELECTED].red   = (color.red   + 3*ctree_style->text[GTK_STATE_SELECTED].red  ) / 4;
6033 		style->text[GTK_STATE_SELECTED].green = (color.green + 3*ctree_style->text[GTK_STATE_SELECTED].green) / 4;
6034 		style->text[GTK_STATE_SELECTED].blue  = (color.blue  + 3*ctree_style->text[GTK_STATE_SELECTED].blue ) / 4;
6035 	}
6036 
6037 	gtk_cmctree_node_set_row_style(ctree, node, style);
6038 	g_object_unref(style);
6039 }
6040 
summary_set_row_colorlabel(SummaryView * summaryview,GtkCMCTreeNode * row,guint labelcolor)6041 static void summary_set_row_colorlabel(SummaryView *summaryview, GtkCMCTreeNode *row, guint labelcolor)
6042 {
6043 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6044 	MsgInfo *msginfo;
6045 
6046 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6047 	cm_return_if_fail(msginfo);
6048 
6049 	summary_msginfo_change_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(labelcolor), 0,
6050 					MSG_CLABEL_FLAG_MASK, 0);
6051 	summary_set_row_marks(summaryview, row);
6052 }
6053 
summary_set_colorlabel(SummaryView * summaryview,guint labelcolor,GtkWidget * widget)6054 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
6055 			    GtkWidget *widget)
6056 {
6057 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6058 	GList *cur;
6059 	gboolean froze = FALSE;
6060 
6061 	if (prefs_common.ask_override_colorlabel) {
6062 		GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6063 		gboolean ask = FALSE;
6064 		AlertValue val;
6065 		guint color;
6066 		gboolean already_this_color_everywhere = TRUE;
6067 
6068 		/* if clearing color labels (applying 'none', 0):
6069 		    - ask if at least one message has a non-0 color label set
6070 		   if applying a non-0 color label:
6071 		    - ask if at least one of the selected messages has a non-0 color label different
6072 			  from the one we want to apply.
6073 		    - don't ask if all messages have the same color label as the one we're applying
6074 		*/
6075 		for (cur = GTK_CMCLIST(ctree)->selection;
6076 			 !ask && cur != NULL && cur->data != NULL;
6077 			 cur = cur->next) {
6078 			MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(cur->data));
6079 			if (msginfo) {
6080 				color = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6081 				if (labelcolor == 0) {
6082 					/* clearing color labels */
6083 					ask = (color != 0);
6084 				} else {
6085 					already_this_color_everywhere &= (color == labelcolor);
6086 					ask = ((color != 0) && (color != labelcolor)) && !already_this_color_everywhere;
6087 				}
6088 			}
6089 		}
6090 
6091 		if (ask) {
6092 			gchar *msg;
6093 
6094 			if (labelcolor == 0)
6095 				msg = _("Do you really want to reset the color label of all selected messages?");
6096 			else
6097 				msg = _("Do you really want to apply this color label to all selected messages?");
6098 			val = alertpanel_full(labelcolor == 0? _("Reset color label"): _("Set color label"), msg,
6099 				  GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
6100 				  TRUE, NULL, ALERT_QUESTION);
6101 
6102 			if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
6103 				return;
6104 			else if (val & G_ALERTDISABLE)
6105 				prefs_common.ask_override_colorlabel = FALSE;
6106 		}
6107 	}
6108 
6109 	START_LONG_OPERATION(summaryview, FALSE);
6110 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
6111 		summary_set_row_colorlabel(summaryview,
6112 					   GTK_CMCTREE_NODE(cur->data), labelcolor);
6113 	END_LONG_OPERATION(summaryview);
6114 }
6115 
summary_set_row_tag(SummaryView * summaryview,GtkCMCTreeNode * row,gboolean refresh,gboolean set,gint id)6116 static gboolean summary_set_row_tag(SummaryView *summaryview, GtkCMCTreeNode *row, gboolean refresh, gboolean set, gint id)
6117 {
6118 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6119 	MsgInfo *msginfo;
6120 	gchar *tags_str = NULL;
6121 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6122 	cm_return_val_if_fail(msginfo, FALSE);
6123 
6124 	procmsg_msginfo_update_tags(msginfo, set, id);
6125 
6126 	if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
6127 		tags_str = procmsg_msginfo_get_tags_str(msginfo);
6128 		gtk_cmctree_node_set_text(ctree, row,
6129 				summaryview->col_pos[S_COL_TAGS],
6130 				tags_str?tags_str:"-");
6131 		g_free(tags_str);
6132 	}
6133 
6134 	summary_set_row_marks(summaryview, row);
6135 	if (row == summaryview->displayed) {
6136 		return TRUE;
6137 	}
6138 	return FALSE;
6139 }
6140 
summary_set_tag(SummaryView * summaryview,gint tag_id,GtkWidget * widget)6141 void summary_set_tag(SummaryView *summaryview, gint tag_id,
6142 			    GtkWidget *widget)
6143 {
6144 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6145 	GList *cur;
6146 	gboolean set = tag_id > 0;
6147 	gint real_id = set? tag_id:-tag_id;
6148 	gboolean froze = FALSE;
6149 	gboolean redisplay = FALSE;
6150 
6151 	if (summary_is_locked(summaryview))
6152 		return;
6153 	START_LONG_OPERATION(summaryview, FALSE);
6154 	folder_item_set_batch(summaryview->folder_item, TRUE);
6155 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
6156 		redisplay |= summary_set_row_tag(summaryview,
6157 					   GTK_CMCTREE_NODE(cur->data), FALSE, set, real_id);
6158 	}
6159 	folder_item_set_batch(summaryview->folder_item, FALSE);
6160 	END_LONG_OPERATION(summaryview);
6161 	if (redisplay)
6162 		summary_redisplay_msg(summaryview);
6163 }
6164 
summary_tags_menu_item_activate_cb(GtkWidget * widget,gpointer data)6165 static void summary_tags_menu_item_activate_cb(GtkWidget *widget,
6166 						     gpointer data)
6167 {
6168 	gint id = GPOINTER_TO_INT(data);
6169 	gboolean set = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
6170 	SummaryView *summaryview;
6171 
6172 	summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6173 	cm_return_if_fail(summaryview != NULL);
6174 
6175 	/* "dont_toggle" state set? */
6176 	if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6177 				"dont_toggle"))
6178 		return;
6179 
6180 	if (!set)
6181 		id = -id;
6182 	summary_set_tag(summaryview, id, NULL);
6183 }
6184 
summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem * menu_item,gpointer data)6185 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6186 							  gpointer data)
6187 {
6188 	SummaryView *summaryview;
6189 	GtkMenuShell *menu;
6190 	GtkCheckMenuItem **items;
6191 	gint n;
6192 	GList *children, *cur, *sel;
6193 
6194 	summaryview = (SummaryView *)data;
6195 	cm_return_if_fail(summaryview != NULL);
6196 
6197 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
6198 	if (!sel) return;
6199 
6200 	menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
6201 
6202 	cm_return_if_fail(menu != NULL);
6203 
6204 	Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
6205 
6206 	/* NOTE: don't return prematurely because we set the "dont_toggle"
6207 	 * state for check menu items */
6208 	g_object_set_data(G_OBJECT(menu), "dont_toggle",
6209 			  GINT_TO_POINTER(1));
6210 
6211 	/* clear items. get item pointers. */
6212 	children = gtk_container_get_children(GTK_CONTAINER(menu));
6213 	for (n = 0, cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6214 		if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6215 			gtk_check_menu_item_set_active
6216 				(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6217 			items[n] = GTK_CHECK_MENU_ITEM(cur->data);
6218 			n++;
6219 		}
6220 	}
6221 
6222 	g_list_free(children);
6223 
6224 	if (n == (N_COLOR_LABELS + 1)) {
6225 		/* iterate all messages and set the state of the appropriate
6226 		 * items */
6227 		for (; sel != NULL; sel = sel->next) {
6228 			MsgInfo *msginfo;
6229 			gint clabel;
6230 
6231 			msginfo = gtk_cmctree_node_get_row_data
6232 				(GTK_CMCTREE(summaryview->ctree),
6233 				 GTK_CMCTREE_NODE(sel->data));
6234 			if (msginfo) {
6235 				clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6236 				if (!gtk_check_menu_item_get_active(items[clabel]))
6237 					gtk_check_menu_item_set_active
6238 						(items[clabel], TRUE);
6239 			}
6240 		}
6241 	} else
6242 		g_warning("invalid number of color elements (%d)", n);
6243 
6244 	/* reset "dont_toggle" state */
6245 	g_object_set_data(G_OBJECT(menu), "dont_toggle",
6246 			  GINT_TO_POINTER(0));
6247 }
6248 
summary_colorlabel_menu_create(SummaryView * summaryview,gboolean refresh)6249 static void summary_colorlabel_menu_create(SummaryView *summaryview, gboolean refresh)
6250 {
6251 	GtkWidget *label_menuitem;
6252 	GtkWidget *menu;
6253 	GtkWidget *item;
6254 	gint i;
6255 	gchar *accel_path = NULL;
6256 
6257 	label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/ColorLabel");
6258 	g_signal_connect(G_OBJECT(label_menuitem), "activate",
6259 			 G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
6260 			   summaryview);
6261 	gtk_widget_show(label_menuitem);
6262 
6263 	menu = gtk_menu_new();
6264 
6265 	gtk_menu_set_accel_group (GTK_MENU (menu),
6266 		gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager));
6267 
6268 	/* create sub items. for the menu item activation callback we pass the
6269 	 * index of label_colors[] as data parameter. for the None color we
6270 	 * pass an invalid (high) value. also we attach a data pointer so we
6271 	 * can always get back the SummaryView pointer. */
6272 
6273 	item = gtk_check_menu_item_new_with_label(_("None"));
6274 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6275 	g_signal_connect(G_OBJECT(item), "activate",
6276 			 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6277 			   GUINT_TO_POINTER(0));
6278 	g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
6279 	gtk_widget_show(item);
6280 
6281 	accel_path = g_strdup_printf("<ClawsColorLabels>/None");
6282 	gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6283 	g_free(accel_path);
6284 	gtk_accel_map_add_entry("<ClawsColorLabels>/None", GDK_KEY_0, GDK_CONTROL_MASK);
6285 
6286 	item = gtk_menu_item_new();
6287 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6288 	gtk_widget_show(item);
6289 
6290 	/* create pixmap/label menu items */
6291 	for (i = 0; i < N_COLOR_LABELS; i++) {
6292 		item = colorlabel_create_check_color_menu_item(
6293 			i, refresh, SUMMARY_COLORMENU);
6294 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6295 		g_signal_connect(G_OBJECT(item), "activate",
6296 				 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6297 				 GUINT_TO_POINTER(i + 1));
6298 		g_object_set_data(G_OBJECT(item), "summaryview",
6299 				  summaryview);
6300 		gtk_widget_show(item);
6301 		accel_path = g_strdup_printf("<ClawsColorLabels>/%d", i+1);
6302 		gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6303 		if (i < 9)
6304 			gtk_accel_map_add_entry(accel_path, GDK_KEY_1+i, GDK_CONTROL_MASK);
6305 		g_free(accel_path);
6306 		g_signal_connect (gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager),
6307 			"accel-changed", G_CALLBACK (mainwin_accel_changed_cb), item);
6308 	}
6309 
6310 	gtk_widget_show(menu);
6311 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6312 	summaryview->colorlabel_menu = menu;
6313 }
6314 
summary_tags_menu_item_activate_item_cb(GtkMenuItem * menu_item,gpointer data)6315 static void summary_tags_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6316 							  gpointer data)
6317 {
6318 	GtkMenuShell *menu;
6319 	GList *children, *cur;
6320 	GList *sel;
6321 	GHashTable *menu_table = g_hash_table_new_full(
6322 					g_direct_hash,
6323 					g_direct_equal,
6324 					NULL, NULL);
6325 	GHashTable *menu_allsel_table = g_hash_table_new_full(
6326 					g_direct_hash,
6327 					g_direct_equal,
6328 					NULL, NULL);
6329 	gint sel_len;
6330 	SummaryView *summaryview = (SummaryView *)data;
6331 	cm_return_if_fail(summaryview != NULL);
6332 
6333 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
6334 	if (!sel) return;
6335 
6336 	menu = GTK_MENU_SHELL(summaryview->tags_menu);
6337 	cm_return_if_fail(menu != NULL);
6338 
6339 	/* NOTE: don't return prematurely because we set the "dont_toggle"
6340 	 * state for check menu items */
6341 	g_object_set_data(G_OBJECT(menu), "dont_toggle",
6342 			  GINT_TO_POINTER(1));
6343 
6344 	/* clear items. get item pointers. */
6345 	children = gtk_container_get_children(GTK_CONTAINER(menu));
6346 	for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6347 		if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6348 			gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6349 				"tag_id"));
6350 			gtk_check_menu_item_set_active
6351 				(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6352 
6353 			g_hash_table_insert(menu_table, GINT_TO_POINTER(id), GTK_CHECK_MENU_ITEM(cur->data));
6354 			g_hash_table_insert(menu_allsel_table, GINT_TO_POINTER(id), GINT_TO_POINTER(0));
6355 		}
6356 	}
6357 
6358 	g_list_free(children);
6359 
6360 	/* iterate all messages and set the state of the appropriate
6361 	 * items */
6362 	sel_len = 0;
6363 	for (; sel != NULL; sel = sel->next) {
6364 		MsgInfo *msginfo;
6365 		GSList *tags = NULL;
6366 		GtkCheckMenuItem *item;
6367 		msginfo = gtk_cmctree_node_get_row_data
6368 			(GTK_CMCTREE(summaryview->ctree),
6369 			 GTK_CMCTREE_NODE(sel->data));
6370 		sel_len++;
6371 		if (msginfo) {
6372 			tags =  msginfo->tags;
6373 			if (!tags)
6374 				continue;
6375 
6376 			for (; tags; tags = tags->next) {
6377 				gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, tags->data));
6378 				item = g_hash_table_lookup(menu_table, GINT_TO_POINTER(tags->data));
6379 				if (item && !gtk_check_menu_item_get_active(item)) {
6380 					gtk_check_menu_item_set_active
6381 						(item, TRUE);
6382 				}
6383 				num_checked++;
6384 				g_hash_table_replace(menu_allsel_table, tags->data, GINT_TO_POINTER(num_checked));
6385 			}
6386 		}
6387 	}
6388 
6389 	children = gtk_container_get_children(GTK_CONTAINER(menu));
6390 	for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6391 		if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6392 			gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6393 				"tag_id"));
6394 			gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, GINT_TO_POINTER(id)));
6395 			if (num_checked < sel_len && num_checked > 0)
6396 				gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), TRUE);
6397 			else
6398 				gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6399 		}
6400 	}
6401 	g_list_free(children);
6402 	g_hash_table_destroy(menu_table);
6403 	g_hash_table_destroy(menu_allsel_table);
6404 	/* reset "dont_toggle" state */
6405 	g_object_set_data(G_OBJECT(menu), "dont_toggle",
6406 			  GINT_TO_POINTER(0));
6407 }
6408 
summaryview_destroy(SummaryView * summaryview)6409 void summaryview_destroy(SummaryView *summaryview)
6410 {
6411 	if(summaryview->simplify_subject_preg) {
6412 		regfree(summaryview->simplify_subject_preg);
6413 		g_free(summaryview->simplify_subject_preg);
6414 		summaryview->simplify_subject_preg = NULL;
6415 	}
6416 }
summary_tags_menu_item_apply_tags_activate_cb(GtkWidget * widget,gpointer data)6417 static void summary_tags_menu_item_apply_tags_activate_cb(GtkWidget *widget,
6418 						     gpointer data)
6419 {
6420 	SummaryView *summaryview;
6421 
6422 	summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6423 	cm_return_if_fail(summaryview != NULL);
6424 
6425 	/* "dont_toggle" state set? */
6426 	if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6427 				"dont_toggle"))
6428 		return;
6429 
6430 	tag_apply_open(summary_get_selection(summaryview));
6431 }
6432 
summary_tag_cmp_list(gconstpointer a,gconstpointer b)6433 static gint summary_tag_cmp_list(gconstpointer a, gconstpointer b)
6434 {
6435 	gint id_a = GPOINTER_TO_INT(a);
6436 	gint id_b = GPOINTER_TO_INT(b);
6437 	const gchar *tag_a = tags_get_tag(id_a);
6438 	const gchar *tag_b = tags_get_tag(id_b);
6439 
6440 	if (tag_a == NULL)
6441 		return tag_b == NULL ? 0:1;
6442 
6443 	if (tag_b == NULL)
6444 		return 1;
6445 
6446 	return g_utf8_collate(tag_a, tag_b);
6447 }
6448 
summary_tags_menu_create(SummaryView * summaryview,gboolean refresh)6449 static void summary_tags_menu_create(SummaryView *summaryview, gboolean refresh)
6450 {
6451 
6452 	GtkWidget *label_menuitem;
6453 	GtkWidget *menu;
6454 	GtkWidget *item;
6455 	GSList *cur = tags_get_list();
6456 	GSList *orig = NULL;
6457 	gboolean existing_tags = FALSE;
6458 	gchar *accel_path = NULL;
6459 
6460 	cur = orig = g_slist_sort(cur, summary_tag_cmp_list);
6461 	label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/Tags");
6462 	g_signal_connect(G_OBJECT(label_menuitem), "activate",
6463 			 G_CALLBACK(summary_tags_menu_item_activate_item_cb),
6464 			   summaryview);
6465 
6466 	gtk_widget_show(label_menuitem);
6467 
6468 	menu = gtk_menu_new();
6469 
6470 	gtk_menu_set_accel_group (GTK_MENU (menu),
6471 		gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager));
6472 
6473 	/* create tags menu items */
6474 	for (; cur; cur = cur->next) {
6475 		gint id = GPOINTER_TO_INT(cur->data);
6476 		const gchar *tag = tags_get_tag(id);
6477 		item = gtk_check_menu_item_new_with_label(tag);
6478 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6479 		g_signal_connect(G_OBJECT(item), "activate",
6480 				 G_CALLBACK(summary_tags_menu_item_activate_cb),
6481 				 GINT_TO_POINTER(id));
6482 		g_object_set_data(G_OBJECT(item), "summaryview",
6483 				  summaryview);
6484 		g_object_set_data(G_OBJECT(item), "tag_id",
6485 				  GINT_TO_POINTER(id));
6486 		gtk_widget_show(item);
6487 		accel_path = g_strconcat("<ClawsTags>/",tag, NULL);
6488 		gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6489 		g_free(accel_path);
6490 		existing_tags = TRUE;
6491 	}
6492 	if (existing_tags) {
6493 		/* separator */
6494 		item = gtk_menu_item_new();
6495 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6496 		gtk_widget_show(item);
6497 	}
6498 
6499 	item = gtk_menu_item_new_with_label(_("Apply tags..."));
6500 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6501 	g_signal_connect(G_OBJECT(item), "activate",
6502 			 G_CALLBACK(summary_tags_menu_item_apply_tags_activate_cb),
6503 			 NULL);
6504 	g_object_set_data(G_OBJECT(item), "summaryview",
6505 			  summaryview);
6506 	gtk_widget_show(item);
6507 	accel_path = g_strdup_printf("<ClawsTags>/ApplyTags");
6508 	gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6509 	g_free(accel_path);
6510 
6511 	g_slist_free(orig);
6512 	gtk_widget_show(menu);
6513 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6514 	summaryview->tags_menu = menu;
6515 }
6516 
summary_popup_menu(GtkWidget * widget,gpointer data)6517 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
6518 {
6519 	SummaryView *summaryview = (SummaryView *)data;
6520 	summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6521 
6522 	gtk_menu_popup(GTK_MENU(summaryview->popupmenu),
6523 		       NULL, NULL, NULL, NULL,
6524 		       3, gtk_get_current_event_time());
6525 
6526 	return TRUE;
6527 }
6528 
6529 #if !GENERIC_UMPC
summaryview_get_tooltip_text(SummaryView * summaryview,MsgInfo * info,gint column)6530 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
6531 {
6532 	MsgFlags flags;
6533 	if (!info)
6534 		return NULL;
6535 
6536 	flags = info->flags;
6537 
6538 	switch(summaryview->col_state[column].type) {
6539 		case S_COL_STATUS:
6540 			if (MSG_IS_IGNORE_THREAD(flags)) {
6541 				return _("Ignored thread");
6542 			} else if (MSG_IS_WATCH_THREAD(flags)) {
6543 				return _("Watched thread");
6544 			} else if (MSG_IS_SPAM(flags)) {
6545 				return _("Spam");
6546 			} else if (MSG_IS_NEW(flags)) {
6547 				return _("New");
6548 			} else if (MSG_IS_UNREAD(flags)) {
6549 				return _("Unread");
6550 			} else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
6551 				return _("Replied but also forwarded - click to see reply");
6552 			} else if (MSG_IS_REPLIED(flags)) {
6553 				return _("Replied - click to see reply");
6554 			} else if (MSG_IS_FORWARDED(flags)) {
6555 				return _("Forwarded");
6556 			} else {
6557 				return NULL;
6558 			}
6559 		case S_COL_MARK:
6560 			if (MSG_IS_DELETED(flags)) {
6561 				return _("Deleted");
6562 			} else if (MSG_IS_MARKED(flags)) {
6563 				return _("Marked");
6564 			} else if (MSG_IS_MOVE(flags)) {
6565 				return _("To be moved");
6566 			} else if (MSG_IS_COPY(flags)) {
6567 				return _("To be copied");
6568 			} else {
6569 				return NULL;
6570 			}
6571 		case S_COL_LOCKED:
6572 			if (MSG_IS_LOCKED(flags)) {
6573 				return _("Locked");
6574 			} else {
6575 				return NULL;
6576 			}
6577 		case S_COL_MIME:
6578 			if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
6579 				return _("Signed, has attachment(s)");
6580 			} else if (MSG_IS_SIGNED(flags)) {
6581 				return _("Signed");
6582 			} else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
6583 				return _("Encrypted, has attachment(s)");
6584 			} else if (MSG_IS_ENCRYPTED(flags)) {
6585 				return _("Encrypted");
6586 			} else if (MSG_IS_WITH_ATTACHMENT(flags)) {
6587 				return _("Has attachment(s)");
6588 			} else {
6589 				return NULL;
6590 			}
6591 		default:
6592 			return NULL;
6593 	}
6594 }
tooltip_cb(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,gpointer user_data)6595 static gboolean tooltip_cb (GtkWidget  *widget,
6596                             gint        x,
6597                             gint        y,
6598                             gboolean    keyboard_mode,
6599                             GtkTooltip *tooltip,
6600                             gpointer    user_data)
6601 {
6602 	GtkCMCTree *ctree = GTK_CMCTREE(widget);
6603 	SummaryView *summaryview = (SummaryView *)user_data;
6604 	gint row = -1, column = -1;
6605 	int offset = prefs_common.show_col_headers ? 24:0;
6606 	GtkCMCTreeNode *node = NULL;
6607 	gchar *text = NULL;
6608 	gchar *formatted = NULL;
6609 	MsgInfo *info = NULL;
6610 	GdkRectangle rect;
6611 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6612 	gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6613 	if (!prefs_common.show_tooltips)
6614 		return FALSE;
6615 
6616 	if (y - offset < 0)
6617 		return FALSE;
6618 
6619 	if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset,
6620 					  &row, &column))
6621 		return FALSE;
6622 
6623 	if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL)
6624 		return FALSE;
6625 
6626 	if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL)
6627 		return FALSE;
6628 
6629 	switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) {
6630 		case GTK_CMCELL_TEXT:
6631 			if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE)
6632 				return FALSE;
6633 			break;
6634 		case GTK_CMCELL_PIXTEXT:
6635 			if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text,
6636 				NULL, NULL) != TRUE)
6637 				return FALSE;
6638 			break;
6639 		default:
6640 			if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
6641 				return FALSE;
6642 	}
6643 
6644 	if (!text || !*text)
6645 		return FALSE;
6646 
6647 	formatted = g_strdup(text);
6648 	g_strstrip(formatted);
6649 
6650 	if ((vert_layout || small_layout) && prefs_common.two_line_vert)
6651 		gtk_tooltip_set_markup (tooltip, formatted);
6652 	else
6653 	                gtk_tooltip_set_text (tooltip, formatted);
6654 	g_free(formatted);
6655 
6656 	rect.x = x - 2;
6657 	rect.y = y - 2;
6658 	rect.width = 12;
6659 	rect.height= 12;
6660 	gtk_tooltip_set_tip_area(tooltip, &rect);
6661 
6662 	return TRUE;
6663 }
6664 #endif
6665 
summary_header_button_pressed(GtkWidget * widget,GdkEvent * _event,gpointer user_data)6666 static gboolean summary_header_button_pressed(GtkWidget *widget,
6667 		GdkEvent *_event,
6668 		gpointer user_data)
6669 {
6670 	GdkEventButton *event = (GdkEventButton *)_event;
6671 	SummaryView *summaryview = (SummaryView *)user_data;
6672 
6673 	cm_return_val_if_fail(summaryview != NULL, FALSE);
6674 
6675 	/* Only handle single button presses. */
6676 	if (event->type == GDK_2BUTTON_PRESS ||
6677 			event->type == GDK_3BUTTON_PRESS)
6678 		return FALSE;
6679 
6680 	/* Handle right-click for context menu */
6681 	if (event->button == 3) {
6682 		/* Set up any menu items that need setting up. */
6683 		summaryview->header_menu_lock = TRUE;
6684 		cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager,
6685 				"Menus/SummaryViewHeaderPopup/LockColumnHeaders",
6686 				prefs_common_get_prefs()->summary_col_lock);
6687 		summaryview->header_menu_lock = FALSE;
6688 
6689 		gtk_menu_popup(GTK_MENU(summaryview->headerpopupmenu),
6690 				NULL, NULL, NULL, NULL, 3, event->time);
6691 		return TRUE;
6692 	}
6693 
6694 	return FALSE;
6695 }
6696 
summary_ctree_create(SummaryView * summaryview)6697 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
6698 {
6699 	GtkWidget *ctree;
6700 	gint *col_pos = summaryview->col_pos;
6701 	SummaryColumnState *col_state;
6702 	gchar *titles[N_SUMMARY_COLS];
6703 	SummaryColumnType type;
6704 	gint pos;
6705 	gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6706 	gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6707 	memset(titles, 0, sizeof(titles));
6708 
6709 	col_state = prefs_summary_column_get_config();
6710 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6711 		summaryview->col_state[pos] = col_state[pos];
6712 		type = col_state[pos].type;
6713 		col_pos[type] = pos;
6714 		titles[pos] = "dummy";
6715 	}
6716 	col_state = summaryview->col_state;
6717 
6718 	ctree = gtk_sctree_new_with_titles
6719 		(N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
6720 
6721 	if (prefs_common.show_col_headers == FALSE)
6722 		gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
6723 
6724 	gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_MULTIPLE);
6725 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6726 					   GTK_JUSTIFY_CENTER);
6727 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6728 					   GTK_JUSTIFY_CENTER);
6729 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6730 					   GTK_JUSTIFY_CENTER);
6731 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6732 					   GTK_JUSTIFY_CENTER);
6733 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6734 					   GTK_JUSTIFY_RIGHT);
6735 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6736 					   GTK_JUSTIFY_RIGHT);
6737 	gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6738 					   GTK_JUSTIFY_RIGHT);
6739 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6740 				   prefs_common.summary_col_size[S_COL_MARK]);
6741 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6742 				   prefs_common.summary_col_size[S_COL_STATUS]);
6743 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6744 				   prefs_common.summary_col_size[S_COL_LOCKED]);
6745 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6746 				   prefs_common.summary_col_size[S_COL_MIME]);
6747 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT],
6748 				   prefs_common.summary_col_size[S_COL_SUBJECT]);
6749 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM],
6750 				   prefs_common.summary_col_size[S_COL_FROM]);
6751 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO],
6752 				   prefs_common.summary_col_size[S_COL_TO]);
6753 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE],
6754 				   prefs_common.summary_col_size[S_COL_DATE]);
6755 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6756 				   prefs_common.summary_col_size[S_COL_SIZE]);
6757 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6758 				   prefs_common.summary_col_size[S_COL_NUMBER]);
6759 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6760 				   prefs_common.summary_col_size[S_COL_SCORE]);
6761 	gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS],
6762 				   prefs_common.summary_col_size[S_COL_TAGS]);
6763 
6764 	gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
6765 			     GTK_CMCTREE_EXPANDER_TRIANGLE);
6766 
6767 	gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
6768 
6769 	gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12);
6770 	g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
6771 
6772 	for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6773 		gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[pos].button,
6774 				       FALSE);
6775 		if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6776 		     (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6777 		     pos == summaryview->col_pos[S_COL_DATE]) && (vert_layout || small_layout) &&
6778 			    prefs_common.two_line_vert)
6779 			gtk_cmclist_set_column_visibility
6780 				(GTK_CMCLIST(ctree), pos, FALSE);
6781 		else
6782 			gtk_cmclist_set_column_visibility
6783 				(GTK_CMCLIST(ctree), pos, col_state[pos].visible);
6784 	}
6785 	if (prefs_common.two_line_vert)
6786 		gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert_layout||small_layout);
6787 
6788 	/* connect signal to the buttons for sorting */
6789 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
6790 	g_signal_connect \
6791 		(G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6792 		 "button-press-event", \
6793 		 G_CALLBACK(summary_header_button_pressed), \
6794 		 summaryview); \
6795 	g_signal_connect \
6796 		(G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6797 		 "clicked", \
6798 		 G_CALLBACK(func), \
6799 		 summaryview);
6800 
6801 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK   , summary_mark_clicked)
6802 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked)
6803 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME   , summary_mime_clicked)
6804 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked)
6805 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE   , summary_size_clicked)
6806 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE   , summary_date_clicked)
6807 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM   , summary_from_clicked)
6808 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO     , summary_to_clicked)
6809 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked)
6810 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE  , summary_score_clicked)
6811 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED , summary_locked_clicked)
6812 	CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TAGS   , summary_tags_clicked)
6813 
6814 #undef CLIST_BUTTON_SIGNAL_CONNECT
6815 
6816 	g_signal_connect(G_OBJECT(ctree), "tree_select_row",
6817 			 G_CALLBACK(summary_selected), summaryview);
6818 	g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
6819 			 G_CALLBACK(summary_unselected), summaryview);
6820 	g_signal_connect(G_OBJECT(ctree), "button_press_event",
6821 			 G_CALLBACK(summary_button_pressed),
6822 			 summaryview);
6823 	g_signal_connect(G_OBJECT(ctree), "popup-menu",
6824 			 G_CALLBACK(summary_popup_menu), summaryview);
6825 	g_signal_connect(G_OBJECT(ctree), "button_release_event",
6826 			 G_CALLBACK(summary_button_released),
6827 			 summaryview);
6828 	g_signal_connect(G_OBJECT(ctree), "key_press_event",
6829 			 G_CALLBACK(summary_key_pressed), summaryview);
6830 	g_signal_connect(G_OBJECT(ctree), "resize_column",
6831 			 G_CALLBACK(summary_col_resized), summaryview);
6832         g_signal_connect(G_OBJECT(ctree), "open_row",
6833 			 G_CALLBACK(summary_open_row), summaryview);
6834 
6835 	g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
6836 			       G_CALLBACK(summary_tree_expanded),
6837 			       summaryview);
6838 	g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
6839 			       G_CALLBACK(summary_tree_collapsed),
6840 			       summaryview);
6841 
6842 	g_signal_connect(G_OBJECT(ctree), "start_drag",
6843 			 G_CALLBACK(summary_start_drag),
6844 			 summaryview);
6845 	g_signal_connect(G_OBJECT(ctree), "drag_data_get",
6846 			 G_CALLBACK(summary_drag_data_get),
6847 			 summaryview);
6848 	g_signal_connect(G_OBJECT(ctree), "drag_end",
6849 			 G_CALLBACK(summary_drag_end),
6850 			 summaryview);
6851 
6852 	gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
6853 			  summary_drag_types, 3,
6854 			  GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
6855 
6856 	g_signal_connect(G_OBJECT(ctree), "drag_data_received",
6857 			 G_CALLBACK(summary_drag_data_received),
6858 			 summaryview);
6859 
6860 	g_signal_connect(G_OBJECT(ctree), "drag_motion",
6861 			 G_CALLBACK(summary_drag_motion_cb),
6862 			 summaryview);
6863 
6864 #if !GENERIC_UMPC
6865 	g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
6866 	g_signal_connect(G_OBJECT(ctree), "query-tooltip",
6867 			 G_CALLBACK(tooltip_cb),
6868 			summaryview);
6869 #endif
6870 	return ctree;
6871 }
6872 
summary_set_column_order(SummaryView * summaryview)6873 void summary_set_column_order(SummaryView *summaryview)
6874 {
6875 	GtkWidget *ctree;
6876 	GtkWidget *scrolledwin = summaryview->scrolledwin;
6877 	FolderItem *item;
6878 	guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
6879 	guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
6880 
6881 	item = summaryview->folder_item;
6882 
6883 	summary_clear_all(summaryview);
6884 	gtk_widget_destroy(summaryview->ctree);
6885 
6886 	summaryview->ctree = ctree = summary_ctree_create(summaryview);
6887 	summary_set_fonts(summaryview);
6888 	summary_set_column_titles(summaryview);
6889 	gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6890 					    GTK_CMCLIST(ctree)->hadjustment);
6891 	gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6892 					    GTK_CMCLIST(ctree)->vadjustment);
6893 	gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
6894 	gtk_widget_show(ctree);
6895 
6896 	summary_show(summaryview, item, FALSE);
6897 
6898 	summary_select_by_msgnum(summaryview, selected_msgnum, FALSE);
6899 
6900 	summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum);
6901 	summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
6902 	if (!summaryview->displayed)
6903 		messageview_clear(summaryview->messageview);
6904 	else
6905 		summary_redisplay_msg(summaryview);
6906 
6907 	/* get normal row height */
6908 	gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
6909 	normal_row_height = GTK_CMCLIST(ctree)->row_height;
6910 
6911 	if ((prefs_common.layout_mode == SMALL_LAYOUT || prefs_common.layout_mode == VERTICAL_LAYOUT) &&
6912 	    prefs_common.two_line_vert) {
6913 		gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2);
6914 	}
6915 }
6916 
6917 
6918 /* callback functions */
6919 
summary_folder_eventbox_pressed(GtkWidget * eventbox,GdkEventButton * event,SummaryView * summaryview)6920 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event,
6921 					    SummaryView *summaryview)
6922 {
6923 	if (event) {
6924 		folderview_grab_focus(summaryview->folderview);
6925 		mainwindow_exit_folder(summaryview->mainwin);
6926 	}
6927 	return TRUE;
6928 }
6929 
summary_toggle_pressed(GtkWidget * eventbox,GdkEventButton * event,SummaryView * summaryview)6930 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
6931 				   SummaryView *summaryview)
6932 {
6933 	if (event)
6934 		summary_toggle_view(summaryview);
6935 	return TRUE;
6936 }
6937 #ifdef GENERIC_UMPC
summary_toggle_multiple_pressed(GtkWidget * widget,SummaryView * summaryview)6938 static void summary_toggle_multiple_pressed(GtkWidget *widget,
6939 				   SummaryView *summaryview)
6940 {
6941 	GTK_SCTREE(summaryview->ctree)->force_additive_sel =
6942 		gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6943 }
6944 #endif
summary_button_pressed(GtkWidget * ctree,GdkEventButton * event,SummaryView * summaryview)6945 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
6946 				       SummaryView *summaryview)
6947 {
6948 	if (!event) return FALSE;
6949 	if (event->window != GTK_CMCLIST(ctree)->clist_window) return FALSE;
6950 
6951 	if (event->button == 3) {
6952 		/* right clicked */
6953 		summary_set_menu_sensitive(summaryview);
6954 		gtk_menu_popup(GTK_MENU(summaryview->popupmenu), NULL, NULL,
6955 			       NULL, NULL, event->button, event->time);
6956 	} else if (event->button == 2) {
6957 		summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6958 	} else if (event->button == 1) {
6959 		if (!prefs_common.emulate_emacs &&
6960 		    messageview_is_visible(summaryview->messageview))
6961 			summaryview->display_msg = TRUE;
6962 	}
6963 
6964 	return FALSE;
6965 }
6966 
summary_button_released(GtkWidget * ctree,GdkEventButton * event,SummaryView * summaryview)6967 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
6968 					SummaryView *summaryview)
6969 {
6970 	return FALSE;
6971 }
6972 
summary_pass_key_press_event(SummaryView * summaryview,GdkEventKey * event)6973 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
6974 {
6975 	if (!summaryview)
6976 		return FALSE;
6977 	if (summary_is_list(summaryview))
6978 		return summary_key_pressed(summaryview->ctree, event, summaryview);
6979 	else
6980 		return FALSE;
6981 }
6982 
6983 #define BREAK_ON_MODIFIER_KEY() \
6984 	if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
6985 
6986 /* Copied from gtkcmclist.c, if it changes there, it has to change
6987  * here as well. This is an ugly hack, there must be a better way to
6988  * find out how much to move for page up/down. */
6989 #define CELL_SPACING 1
summary_key_pressed(GtkWidget * widget,GdkEventKey * event,SummaryView * summaryview)6990 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
6991 				    SummaryView *summaryview)
6992 {
6993 	GtkCMCTree *ctree = GTK_CMCTREE(widget);
6994 	GtkCMCTreeNode *node;
6995 	MessageView *messageview;
6996 	GtkAdjustment *adj;
6997 	gboolean mod_pressed;
6998 
6999 	if (!event)
7000 		return TRUE;
7001 
7002 	if (quicksearch_has_focus(summaryview->quicksearch))
7003 		return FALSE;
7004 
7005 	messageview = summaryview->messageview;
7006 
7007 	mod_pressed =
7008 		((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
7009 
7010 	if (summaryview->selected) {
7011 		gboolean handled = FALSE;
7012 		switch (event->keyval) {
7013 		case GDK_KEY_space:		/* Page down or go to the next */
7014 			handled = TRUE;
7015 			if (event->state & GDK_CONTROL_MASK) {
7016 				handled = FALSE;
7017 				break;
7018 			}
7019 			if (event->state & GDK_SHIFT_MASK)
7020 				mimeview_scroll_page(messageview->mimeview, TRUE);
7021 			if (summaryview->displayed != summaryview->selected) {
7022 					summary_display_msg(summaryview,
7023 							    summaryview->selected);
7024 			} else {
7025 				if (mod_pressed) {
7026 					if (!mimeview_scroll_page(messageview->mimeview, TRUE))
7027 						summary_select_prev_unread(summaryview);
7028 				} else {
7029 					if (!mimeview_scroll_page(messageview->mimeview, FALSE))
7030 						summary_select_next_unread(summaryview);
7031 				}
7032 			}
7033 			break;
7034 		case GDK_KEY_BackSpace:	/* Page up */
7035 			handled = TRUE;
7036 			mimeview_scroll_page(messageview->mimeview, TRUE);
7037 			break;
7038 		case GDK_KEY_Return:	/* Scroll up/down one line */
7039 		case GDK_KEY_KP_Enter:
7040 			handled = TRUE;
7041 			if (summaryview->displayed != summaryview->selected) {
7042 #ifndef GENERIC_UMPC
7043 				summary_display_msg(summaryview,
7044 						    summaryview->selected);
7045 #else
7046 				summary_open_row(NULL, summaryview);
7047 #endif
7048 				break;
7049 			}
7050 			mimeview_scroll_one_line(messageview->mimeview, mod_pressed);
7051 			break;
7052 		}
7053 
7054 		if (handled)
7055 			return FALSE;
7056 	}
7057 	if (summary_is_locked(summaryview))
7058 		return TRUE;
7059 
7060 	switch (event->keyval) {
7061 	case GDK_KEY_Left:		/* Move focus */
7062 		adj = gtk_scrolled_window_get_hadjustment
7063 			(GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
7064 		if (gtk_adjustment_get_lower(adj) != gtk_adjustment_get_value(adj))
7065 			break;
7066 		/* FALLTHROUGH */
7067 	case GDK_KEY_Escape:
7068 		folderview_grab_focus(summaryview->folderview);
7069 		mainwindow_exit_folder(summaryview->mainwin);
7070 		return TRUE;
7071 	case GDK_KEY_Home: case GDK_KEY_KP_Home:
7072 	case GDK_KEY_End: case GDK_KEY_KP_End:
7073 	case GDK_KEY_Up: case GDK_KEY_KP_Up:
7074 	case GDK_KEY_Down: case GDK_KEY_KP_Down:
7075 	case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
7076 	case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
7077 		if ((node = summaryview->selected) != NULL) {
7078 			GtkCMCTreeNode *next = NULL;
7079 			switch (event->keyval) {
7080 				case GDK_KEY_Home: case GDK_KEY_KP_Home:
7081 					next = gtk_cmctree_node_nth(ctree, 0);
7082 					break;
7083 				case GDK_KEY_End: case GDK_KEY_KP_End:
7084 					next = gtk_cmctree_node_nth(ctree,
7085 							g_list_length(GTK_CMCLIST(ctree)->row_list)-1);
7086 					break;
7087 				case GDK_KEY_Up: case GDK_KEY_KP_Up:
7088 					next = gtk_cmctree_node_nth(ctree,
7089 							MAX(0, GTK_CMCLIST(ctree)->focus_row - 1));
7090 					break;
7091 				case GDK_KEY_Down: case GDK_KEY_KP_Down:
7092 					next = gtk_cmctree_node_nth(ctree,
7093 							MIN(GTK_CMCLIST(ctree)->focus_row + 1, GTK_CMCLIST(ctree)->rows - 1));
7094 					break;
7095 				case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
7096 					next = gtk_cmctree_node_nth(ctree,
7097 							MAX(0, GTK_CMCLIST(ctree)->focus_row -
7098 								(2 * GTK_CMCLIST(ctree)->clist_window_height -
7099 								 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
7100 								(2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
7101 					break;
7102 				case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
7103 					next = gtk_cmctree_node_nth(ctree,
7104 							MIN(GTK_CMCLIST(ctree)->rows - 1, GTK_CMCLIST(ctree)->focus_row +
7105 								(2 * GTK_CMCLIST(ctree)->clist_window_height -
7106 								 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
7107 								(2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
7108 					break;
7109 			}
7110 
7111 			if (next != NULL &&
7112 					next != gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row)) {
7113 				gtk_sctree_select_with_state
7114 					(GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) );
7115 
7116 				/* Deprecated - what are the non-deprecated equivalents? */
7117 				if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL)
7118 					gtkut_ctree_node_move_if_on_the_edge(GTK_CMCTREE(ctree), next, -1);
7119 				if (!mod_pressed)
7120 					summary_select_node(summaryview, summaryview->selected, OPEN_SELECTED_ON_DIRECTIONAL);
7121 				summaryview->selected = next;
7122 			}
7123 		}
7124 		return TRUE;
7125 	default:
7126 		break;
7127 	}
7128 
7129 	if (!summaryview->selected) {
7130 		node = gtk_cmctree_node_nth(ctree, 0);
7131 		if (node)
7132 			gtk_sctree_select(GTK_SCTREE(ctree), node);
7133 		else
7134 			return TRUE;
7135 	}
7136 
7137 	switch (event->keyval) {
7138 	case GDK_KEY_Delete:
7139 		BREAK_ON_MODIFIER_KEY();
7140 		summary_delete_trash(summaryview);
7141 		break;
7142 	default:
7143 		break;
7144 	}
7145 	return FALSE;
7146 }
7147 #undef CELL_SPACING
7148 
quicksearch_execute_cb(QuickSearch * quicksearch,gpointer data)7149 static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
7150 {
7151 	SummaryView *summaryview = data;
7152 
7153 	summaryview_reset_recursive_folder_match(summaryview);
7154 	if (summary_show(summaryview, summaryview->folder_item, FALSE))
7155 		summaryview_quicksearch_recurse(summaryview);
7156 	else
7157 		summaryview_reset_recursive_folder_match(summaryview);
7158 }
7159 
tog_searchbar_cb(GtkWidget * w,gpointer data)7160 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
7161 {
7162 	SummaryView *summaryview = (SummaryView *)data;
7163 
7164 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
7165 		prefs_common.show_searchbar = TRUE;
7166  		quicksearch_show(summaryview->quicksearch);
7167 	} else {
7168 		prefs_common.show_searchbar = FALSE;
7169  		quicksearch_hide(summaryview->quicksearch);
7170 	}
7171 }
7172 
summaryview_activate_quicksearch(SummaryView * summaryview,gboolean show)7173 void summaryview_activate_quicksearch(SummaryView *summaryview, gboolean show)
7174 {
7175 	prefs_common.show_searchbar = show;
7176 	gtk_toggle_button_set_active(
7177 		GTK_TOGGLE_BUTTON(summaryview->toggle_search),
7178 		show);
7179 	if (show) {
7180 		quicksearch_show(summaryview->quicksearch);
7181 	} else {
7182 		quicksearch_hide(summaryview->quicksearch);
7183 		summary_grab_focus(summaryview);
7184 	}
7185 }
7186 
summary_open_row(GtkSCTree * sctree,SummaryView * summaryview)7187 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
7188 {
7189 	if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
7190 		summary_reedit(summaryview);
7191 	else
7192 		summary_open_msg(summaryview);
7193 
7194 	summaryview->display_msg = FALSE;
7195 }
7196 
summary_tree_expanded(GtkCMCTree * ctree,GtkCMCTreeNode * node,SummaryView * summaryview)7197 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7198 				  SummaryView *summaryview)
7199 {
7200 	summary_set_row_marks(summaryview, node);
7201 	if (prefs_common.bold_unread) {
7202 		while (node) {
7203 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7204 			if (GTK_CMCTREE_ROW(node)->children)
7205 				summary_set_row_marks(summaryview, node);
7206 			node = next;
7207 		}
7208 	}
7209 }
7210 
summary_tree_collapsed(GtkCMCTree * ctree,GtkCMCTreeNode * node,SummaryView * summaryview)7211 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7212 				   SummaryView *summaryview)
7213 {
7214 	gtk_sctree_select(GTK_SCTREE(ctree), node);
7215 	summary_set_row_marks(summaryview, node);
7216 }
7217 
summary_unselected(GtkCMCTree * ctree,GtkCMCTreeNode * row,gint column,SummaryView * summaryview)7218 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7219 			       gint column, SummaryView *summaryview)
7220 {
7221 	if (summary_is_locked(summaryview)
7222 	||  GTK_SCTREE(ctree)->selecting_range) {
7223 		return;
7224 	}
7225 
7226 	summary_status_show(summaryview);
7227 }
7228 
summary_selected(GtkCMCTree * ctree,GtkCMCTreeNode * row,gint column,SummaryView * summaryview)7229 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7230 			     gint column, SummaryView *summaryview)
7231 {
7232 	const GList *list, *cur;
7233 	MessageView *msgview;
7234 	MsgInfo *msginfo;
7235 	gboolean marked_unread = FALSE;
7236 
7237 	if (summary_is_locked(summaryview)
7238 	&& !GTK_SCTREE(ctree)->selecting_range
7239 	&& summaryview->messageview
7240 	&& summaryview->messageview->mimeview
7241 	&& summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
7242 	&& summaryview->messageview->mimeview->textview->loading) {
7243 		PostponedSelectData *data = g_new0(PostponedSelectData, 1);
7244 		summaryview->messageview->mimeview->textview->stop_loading = TRUE;
7245 
7246 		data->ctree = ctree;
7247 		data->row = row;
7248 		data->node = NULL;
7249 		data->column = column;
7250 		data->summaryview = summaryview;
7251 		debug_print("postponing open of message till end of load\n");
7252 		g_timeout_add(100, summary_select_retry, data);
7253 		return;
7254 	}
7255 	if (summary_is_locked(summaryview)
7256 	||  GTK_SCTREE(ctree)->selecting_range) {
7257 		return;
7258 	}
7259 
7260 	summary_status_show(summaryview);
7261 
7262 	if (GTK_CMCLIST(ctree)->selection &&
7263 	    GTK_CMCLIST(ctree)->selection->next) {
7264 		summaryview->display_msg = FALSE;
7265 		summary_set_menu_sensitive(summaryview);
7266 		toolbar_main_set_sensitive(summaryview->mainwin);
7267 		return;
7268 	}
7269 
7270 	summaryview->selected = row;
7271 
7272 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7273 	cm_return_if_fail(msginfo != NULL);
7274 
7275 	main_create_mailing_list_menu (summaryview->mainwin, msginfo);
7276 	toolbar_set_learn_button
7277 		(summaryview->mainwin->toolbar,
7278 		 MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
7279 
7280 	switch (column < 0 ? column : summaryview->col_state[column].type) {
7281 	case S_COL_MARK:
7282 		if (!MSG_IS_DELETED(msginfo->flags) &&
7283 		    !MSG_IS_MOVE(msginfo->flags) &&
7284 		    !MSG_IS_COPY(msginfo->flags)) {
7285 			if (MSG_IS_MARKED(msginfo->flags)) {
7286 				summary_unmark_row(summaryview, row);
7287 				summary_status_show(summaryview);
7288 			} else {
7289 				summary_mark_row(summaryview, row);
7290 				summary_status_show(summaryview);
7291 			}
7292 		}
7293 		break;
7294 	case S_COL_STATUS:
7295 		if (MSG_IS_UNREAD(msginfo->flags)) {
7296 			summary_mark_row_as_read(summaryview, row);
7297 			summary_status_show(summaryview);
7298 		} else if (MSG_IS_SPAM(msginfo->flags)) {
7299 				if (procmsg_spam_learner_learn(msginfo, NULL, FALSE) == 0)
7300 					summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
7301 				else
7302 					log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
7303 		} else if (!MSG_IS_REPLIED(msginfo->flags) &&
7304 			 !MSG_IS_FORWARDED(msginfo->flags)) {
7305 			marked_unread = TRUE;
7306 		} else if (MSG_IS_REPLIED(msginfo->flags)) {
7307 			summary_find_answers(summaryview, msginfo);
7308 			return;
7309 		}
7310 		break;
7311 	case S_COL_LOCKED:
7312 		if (MSG_IS_LOCKED(msginfo->flags)) {
7313 			summary_unlock_row(summaryview, row);
7314 			summary_status_show(summaryview);
7315 		}
7316 		else {
7317 			summary_lock_row(summaryview, row);
7318 			summary_status_show(summaryview);
7319 		}
7320 		break;
7321 	default:
7322 		break;
7323 	}
7324 
7325 	list = messageview_get_msgview_list();
7326 	for (cur = list; cur != NULL; cur = cur->next) {
7327 		msgview = (MessageView *) cur->data;
7328 
7329 		if (msgview->new_window && msgview->update_needed) {
7330 			MsgInfo *new_msginfo = summary_get_selected_msg(summaryview);
7331 			messageview_show(msgview, new_msginfo, msgview->all_headers);
7332 			msgview->update_needed = FALSE;
7333 		}
7334 	}
7335 
7336 	if (summaryview->display_msg) {
7337 		summaryview->display_msg = FALSE;
7338 		if (summaryview->displayed != row) {
7339 			summary_display_msg(summaryview, row);
7340 			if (marked_unread) {
7341 				summary_mark_row_as_unread(summaryview, row);
7342 				summary_status_show(summaryview);
7343 			}
7344 			return;
7345 		}
7346 	}
7347 
7348 	if (marked_unread) {
7349 		summary_mark_row_as_unread(summaryview, row);
7350 		summary_status_show(summaryview);
7351 	}
7352 
7353 	summary_set_menu_sensitive(summaryview);
7354 	toolbar_main_set_sensitive(summaryview->mainwin);
7355 }
7356 
summary_col_resized(GtkCMCList * clist,gint column,gint width,SummaryView * summaryview)7357 static void summary_col_resized(GtkCMCList *clist, gint column, gint width,
7358 				SummaryView *summaryview)
7359 {
7360 	SummaryColumnType type = summaryview->col_state[column].type;
7361 
7362 	prefs_common.summary_col_size[type] = width;
7363 }
7364 
7365 
7366 /*
7367  * \brief get List of msginfo selected in SummaryView
7368  *
7369  * \param summaryview
7370  *
7371  * \return GSList holding MsgInfo
7372  */
summary_get_selection(SummaryView * summaryview)7373 GSList *summary_get_selection(SummaryView *summaryview)
7374 {
7375 	GList *sel = NULL;
7376 	GSList *msginfo_list = NULL;
7377 
7378 	cm_return_val_if_fail(summaryview != NULL, NULL);
7379 
7380 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
7381 
7382 	cm_return_val_if_fail(sel != NULL, NULL);
7383 
7384 	for ( ; sel != NULL; sel = sel->next)
7385 		msginfo_list =
7386 			g_slist_prepend(msginfo_list,
7387 				       gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
7388 								   GTK_CMCTREE_NODE(sel->data)));
7389 	return g_slist_reverse(msginfo_list);
7390 }
7391 
7392 /*
7393  * \brief get number of messages currently selected in SummaryView
7394  *
7395  * \param summaryview
7396  *
7397  * \return number of messages currently selected
7398  */
summary_get_selection_count(SummaryView * summaryview)7399 guint summary_get_selection_count(SummaryView *summaryview)
7400 {
7401 	cm_return_val_if_fail(summaryview != NULL, 0);
7402 	return g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
7403 }
7404 
summary_sort_by_column_click(SummaryView * summaryview,FolderSortKey sort_key)7405 static void summary_sort_by_column_click(SummaryView *summaryview,
7406 					 FolderSortKey sort_key)
7407 {
7408 	if (prefs_common.summary_col_lock) {
7409 		debug_print("summaryview columns locked, ignoring\n");
7410 		return;
7411 	}
7412 
7413 	GtkCMCTreeNode *node = NULL;
7414 	START_TIMING("");
7415 	if (summaryview->sort_key == sort_key)
7416 		summary_sort(summaryview, sort_key,
7417 			     summaryview->sort_type == SORT_ASCENDING
7418 			     ? SORT_DESCENDING : SORT_ASCENDING);
7419 	else
7420 		summary_sort(summaryview, sort_key, summaryview->sort_type);
7421 
7422 	node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list);
7423 
7424 	summary_freeze(summaryview);
7425 	if (prefs_common.bold_unread) {
7426 		while (node) {
7427 			GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7428 			if (GTK_CMCTREE_ROW(node)->children)
7429 				summary_set_row_marks(summaryview, node);
7430 			node = next;
7431 		}
7432 	}
7433 	summary_thaw(summaryview);
7434 	END_TIMING();
7435 }
7436 
summary_mark_clicked(GtkWidget * button,SummaryView * summaryview)7437 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
7438 {
7439 	summary_sort_by_column_click(summaryview, SORT_BY_MARK);
7440 }
7441 
summary_status_clicked(GtkWidget * button,SummaryView * summaryview)7442 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
7443 {
7444 	summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
7445 }
7446 
summary_mime_clicked(GtkWidget * button,SummaryView * summaryview)7447 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
7448 {
7449 	summary_sort_by_column_click(summaryview, SORT_BY_MIME);
7450 }
7451 
summary_num_clicked(GtkWidget * button,SummaryView * summaryview)7452 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
7453 {
7454 	summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
7455 }
7456 
summary_size_clicked(GtkWidget * button,SummaryView * summaryview)7457 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
7458 {
7459 	summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
7460 }
7461 
summary_date_clicked(GtkWidget * button,SummaryView * summaryview)7462 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
7463 {
7464 	if (summaryview->sort_key == SORT_BY_THREAD_DATE)
7465 		summary_sort_by_column_click(summaryview, SORT_BY_THREAD_DATE);
7466 	else
7467 		summary_sort_by_column_click(summaryview, SORT_BY_DATE);
7468 }
7469 
summary_from_clicked(GtkWidget * button,SummaryView * summaryview)7470 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
7471 {
7472 	if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
7473 		summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7474 	else
7475 		summary_sort_by_column_click(summaryview, SORT_BY_TO);
7476 }
7477 
summary_to_clicked(GtkWidget * button,SummaryView * summaryview)7478 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
7479 {
7480 	if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
7481 		summary_sort_by_column_click(summaryview, SORT_BY_TO);
7482 	else
7483 		summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7484 }
7485 
summary_subject_clicked(GtkWidget * button,SummaryView * summaryview)7486 static void summary_subject_clicked(GtkWidget *button,
7487 				    SummaryView *summaryview)
7488 {
7489 	summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
7490 }
7491 
summary_score_clicked(GtkWidget * button,SummaryView * summaryview)7492 static void summary_score_clicked(GtkWidget *button,
7493 				  SummaryView *summaryview)
7494 {
7495 	summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
7496 }
7497 
summary_locked_clicked(GtkWidget * button,SummaryView * summaryview)7498 static void summary_locked_clicked(GtkWidget *button,
7499 				   SummaryView *summaryview)
7500 {
7501 	summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
7502 }
7503 
summary_tags_clicked(GtkWidget * button,SummaryView * summaryview)7504 static void summary_tags_clicked(GtkWidget *button,
7505 				   SummaryView *summaryview)
7506 {
7507 	summary_sort_by_column_click(summaryview, SORT_BY_TAGS);
7508 }
7509 
summary_start_drag(GtkWidget * widget,gint button,GdkEvent * event,SummaryView * summaryview)7510 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
7511 			       SummaryView *summaryview)
7512 {
7513 	GdkDragContext *context;
7514 
7515 	cm_return_if_fail(summaryview != NULL);
7516 	cm_return_if_fail(summaryview->folder_item != NULL);
7517 	cm_return_if_fail(summaryview->folder_item->folder != NULL);
7518 
7519 	if (summaryview->selected == NULL) return;
7520 
7521 	context = gtk_drag_begin(widget, summaryview->target_list,
7522 				 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
7523 	gtk_drag_set_icon_default(context);
7524 	if (prefs_common.layout_mode == SMALL_LAYOUT) {
7525 		GtkWidget *paned = gtk_widget_get_parent(GTK_WIDGET_PTR(summaryview));
7526 		if (paned && GTK_IS_PANED(paned)) {
7527 	        	mainwindow_reset_paned(GTK_PANED(paned));
7528 		}
7529 	}
7530 }
7531 
summary_return_to_list(void * data)7532 static gboolean summary_return_to_list(void *data)
7533 {
7534 	SummaryView *summaryview = (SummaryView *)data;
7535 	mainwindow_enter_folder(summaryview->mainwin);
7536 	return FALSE;
7537 }
7538 
summary_drag_end(GtkWidget * widget,GdkDragContext * drag_context,SummaryView * summaryview)7539 static void summary_drag_end	   (GtkWidget	    *widget,
7540 				   GdkDragContext   *drag_context,
7541                                    SummaryView 	    *summaryview)
7542 {
7543 	if (prefs_common.layout_mode == SMALL_LAYOUT) {
7544 		g_timeout_add(250, summary_return_to_list, summaryview);
7545 	}
7546 }
7547 
summary_drag_data_get(GtkWidget * widget,GdkDragContext * drag_context,GtkSelectionData * selection_data,guint info,guint time,SummaryView * summaryview)7548 static void summary_drag_data_get(GtkWidget        *widget,
7549 				  GdkDragContext   *drag_context,
7550 				  GtkSelectionData *selection_data,
7551 				  guint             info,
7552 				  guint             time,
7553 				  SummaryView      *summaryview)
7554 {
7555 	if (info == TARGET_MAIL_URI_LIST) {
7556 		GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7557 		GList *cur;
7558 		MsgInfo *msginfo;
7559 		gchar *mail_list = NULL, *tmp1, *tmp2;
7560 
7561 		for (cur = GTK_CMCLIST(ctree)->selection;
7562 		     cur != NULL && cur->data != NULL; cur = cur->next) {
7563 			msginfo = gtk_cmctree_node_get_row_data
7564 				(ctree, GTK_CMCTREE_NODE(cur->data));
7565 			tmp2 = procmsg_get_message_file(msginfo);
7566 			if (!tmp2) continue;
7567 			if (msginfo->subject) {
7568 				gchar *san_subject = g_strdup(msginfo->subject);
7569 				gchar *dest = NULL;
7570 				subst_for_filename(san_subject);
7571 				dest = g_strdup_printf("%s%s%s.%d.txt",
7572 						get_tmp_dir(),
7573 						G_DIR_SEPARATOR_S,
7574 						san_subject, msginfo->msgnum);
7575 				g_free(san_subject);
7576 				san_subject = g_filename_from_utf8(dest, -1, NULL, NULL, NULL);
7577 				g_free(dest);
7578 				dest = san_subject;
7579 				if (copy_file(tmp2, dest, TRUE) == 0) {
7580 					g_free(tmp2);
7581 					tmp2 = dest;
7582 				}
7583 			}
7584 			tmp1 = g_filename_to_uri(tmp2, NULL, NULL);
7585 			g_free(tmp2);
7586 			tmp2 = g_strconcat(tmp1, "\r\n", NULL);
7587 			g_free(tmp1);
7588 			tmp1 = tmp2;
7589 
7590 			if (!mail_list) {
7591 				mail_list = tmp1;
7592 			} else {
7593 				tmp2 = g_strconcat(mail_list, tmp1, NULL);
7594 				g_free(mail_list);
7595 				g_free(tmp1);
7596 				mail_list = tmp2;
7597 			}
7598 		}
7599 
7600 		if (mail_list != NULL) {
7601 			gtk_selection_data_set(selection_data,
7602 					       gtk_selection_data_get_target(selection_data), 8,
7603 					       mail_list, strlen(mail_list));
7604 			g_free(mail_list);
7605 		}
7606 	} else if (info == TARGET_DUMMY) {
7607 		if (GTK_CMCLIST(summaryview->ctree)->selection)
7608 			gtk_selection_data_set(selection_data,
7609 					       gtk_selection_data_get_target(selection_data), 8,
7610 					       "Dummy-Summaryview",
7611 					       strlen("Dummy-Summaryview")+1);
7612 	} else if (info == TARGET_MAIL_CM_PATH_LIST) {
7613 		/* content: folder_item_identifier\nmsgid1\nmsgid2\nmsgid3 */
7614 
7615 		GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7616 		GList *cur;
7617 		MsgInfo *msginfo;
7618 		gchar *path_list = NULL;
7619 
7620 		/* identifier */
7621 		if(GTK_CMCLIST(ctree)->selection != NULL) {
7622 			msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->selection->data));
7623             if(msginfo && msginfo->folder)
7624               path_list = folder_item_get_identifier(msginfo->folder);
7625 		}
7626 
7627 		for (cur = GTK_CMCLIST(ctree)->selection;
7628 		     cur != NULL && cur->data != NULL; cur = cur->next) {
7629 			gchar *tmp;
7630 
7631 			msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(cur->data));
7632             if(!msginfo)
7633               continue;
7634 			tmp = path_list;
7635 			path_list = g_strconcat(path_list, "\n", (msginfo->msgid ? msginfo->msgid : "unknown"), NULL);
7636 			g_free(tmp);
7637 		}
7638 
7639 		if (path_list != NULL) {
7640 			gtk_selection_data_set(selection_data,
7641 					       gtk_selection_data_get_target(selection_data), 8,
7642 					       path_list, strlen(path_list));
7643 			g_free(path_list);
7644 		}
7645     }
7646 }
7647 
summary_drag_motion_cb(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,SummaryView * summaryview)7648 static gboolean summary_drag_motion_cb(GtkWidget      *widget,
7649 					  GdkDragContext *context,
7650 					  gint            x,
7651 					  gint            y,
7652 					  guint           time,
7653 					  SummaryView	 *summaryview)
7654 {
7655 	FolderItem *item = summaryview->folder_item;
7656 	if (!(item && item->folder && folder_item_parent(item) != NULL
7657 		    && FOLDER_CLASS(item->folder)->add_msg != NULL)) {
7658 		gdk_drag_status(context, 0, time);
7659 		return FALSE;
7660 	} else if (gtk_drag_get_source_widget(context) ==
7661 		mainwindow_get_mainwindow()->folderview->ctree) {
7662 		/* no folders */
7663 		gdk_drag_status(context, 0, time);
7664 		return FALSE;
7665 	} else if (gtk_drag_get_source_widget(context) ==
7666 		summaryview->ctree) {
7667 		/* not from same folder */
7668 		gdk_drag_status(context, 0, time);
7669 		return FALSE;
7670 	} else {
7671 		gdk_drag_status(context, GDK_ACTION_COPY, time);
7672 		return TRUE;
7673 	}
7674 }
7675 
summary_drag_data_received(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time,SummaryView * summaryview)7676 static void summary_drag_data_received(GtkWidget        *widget,
7677 					GdkDragContext   *drag_context,
7678 					gint              x,
7679 					gint              y,
7680 					GtkSelectionData *data,
7681 					guint             info,
7682 					guint             time,
7683 					SummaryView       *summaryview)
7684 {
7685 	if (info == TARGET_MAIL_URI_LIST) {
7686 		FolderItem *item = summaryview->folder_item;
7687 		if (!item) {
7688 			gtk_drag_finish(drag_context, FALSE, FALSE, time);
7689 			return;
7690 		} else {
7691 			folderview_finish_dnd(gtk_selection_data_get_data(data),
7692 				drag_context, time, item);
7693 		}
7694 	}
7695 }
7696 
7697 
7698 /* custom compare functions for sorting */
7699 
summary_cmp_by_date(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7700 static gint summary_cmp_by_date(GtkCMCList *clist,
7701 		      gconstpointer ptr1, gconstpointer ptr2)
7702 {
7703 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7704 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7705 	gint res;
7706 	if (!msginfo1 || !msginfo2)
7707 		return -1;
7708 
7709 	res = (msginfo1->date_t - msginfo2->date_t);
7710 	if (res == 0)
7711 		res = msginfo1->msgnum - msginfo2->msgnum;
7712 	return res;
7713 }
7714 
7715 #define CMP_FUNC_DEF(func_name, val)					 \
7716 static gint func_name(GtkCMCList *clist,				 \
7717 		      gconstpointer ptr1, gconstpointer ptr2)		 \
7718 {									 \
7719 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;		 \
7720 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;		 \
7721 	gint res;							 \
7722 	if (!msginfo1 || !msginfo2)					 \
7723 		return -1;						 \
7724 									 \
7725 	res = (val);							 \
7726 	return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2);	 \
7727 }
7728 
7729 CMP_FUNC_DEF(summary_cmp_by_mark,
7730 	     MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
7731 CMP_FUNC_DEF(summary_cmp_by_status,
7732 	     (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2))
7733 	     - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) )
7734 CMP_FUNC_DEF(summary_cmp_by_mime,
7735 	     MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
7736 CMP_FUNC_DEF(summary_cmp_by_label,
7737 	     MSG_GET_COLORLABEL(msginfo1->flags) -
7738 	     MSG_GET_COLORLABEL(msginfo2->flags))
7739 CMP_FUNC_DEF(summary_cmp_by_locked,
7740 	     MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
7741 
7742 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
7743 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
7744 
7745 #undef CMP_FUNC_DEF
7746 
summary_cmp_by_subject(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7747 static gint summary_cmp_by_subject(GtkCMCList *clist,
7748 				   gconstpointer ptr1,
7749 				   gconstpointer ptr2)
7750 {
7751 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7752 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7753 	gint res;
7754 
7755 	if (!msginfo1->subject)
7756 		return (msginfo2->subject != NULL);
7757 	if (!msginfo2->subject)
7758 		return -1;
7759 
7760 	res = subject_compare_for_sort
7761 		(msginfo1->subject, msginfo2->subject);
7762 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7763 }
7764 
summary_cmp_by_thread_date(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7765 static gint summary_cmp_by_thread_date(GtkCMCList *clist,
7766 				   gconstpointer ptr1,
7767 				   gconstpointer ptr2)
7768 {
7769 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7770 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7771 	gint thread_diff = msginfo1->thread_date - msginfo2->thread_date;
7772 
7773 	if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0)
7774 		return thread_diff;
7775 	else
7776 		return msginfo1->date_t - msginfo2->date_t;
7777 }
7778 
summary_cmp_by_from(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7779 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1,
7780 				gconstpointer ptr2)
7781 {
7782 	const gchar *str1, *str2;
7783 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7784 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7785 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7786 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7787 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7788 	gint res;
7789 
7790 	cm_return_val_if_fail(sv, -1);
7791 	if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) {
7792 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
7793 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
7794 	} else {
7795 		str1 = msginfo1->from;
7796 		str2 = msginfo2->from;
7797 	}
7798 
7799 	if (!str1)
7800 		return str2 != NULL;
7801 
7802 	if (!str2)
7803  		return -1;
7804 
7805 	res = g_utf8_collate(str1, str2);
7806 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7807 }
7808 
summary_cmp_by_to(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7809 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1,
7810 				gconstpointer ptr2)
7811 {
7812 	const gchar *str1, *str2;
7813 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7814 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7815 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7816 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7817 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7818 	gint res;
7819 	cm_return_val_if_fail(sv, -1);
7820 
7821 	if (sv->col_state[sv->col_pos[S_COL_TO]].visible) {
7822 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
7823 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
7824 	} else {
7825 		str1 = msginfo1->to;
7826 		str2 = msginfo2->to;
7827 	}
7828 
7829 	if (!str1)
7830 		return str2 != NULL;
7831 
7832 	if (!str2)
7833  		return -1;
7834 
7835 	res = g_utf8_collate(str1, str2);
7836 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7837 }
7838 
summary_cmp_by_tags(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7839 static gint summary_cmp_by_tags(GtkCMCList *clist, gconstpointer ptr1,
7840 				gconstpointer ptr2)
7841 {
7842 	gchar *str1, *str2;
7843 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7844 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7845 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7846 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7847 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7848 	gint res;
7849 	cm_return_val_if_fail(sv, -1);
7850 
7851 	if (sv->col_state[sv->col_pos[S_COL_TAGS]].visible) {
7852 		str1 = g_strdup(GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TAGS]])->text);
7853 		str2 = g_strdup(GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TAGS]])->text);
7854 	} else {
7855 		str1 = procmsg_msginfo_get_tags_str(msginfo1);
7856 		str2 = procmsg_msginfo_get_tags_str(msginfo2);
7857 	}
7858 
7859 	if (!str1) {
7860 		res = (str2 != NULL);
7861 		g_free(str2);
7862 		return res;
7863 	}
7864 	if (!str2) {
7865 		g_free(str1);
7866  		return -1;
7867 	}
7868 
7869 	res = g_utf8_collate(str1, str2);
7870 	g_free(str1);
7871 	g_free(str2);
7872 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7873 }
7874 
summary_cmp_by_simplified_subject(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7875 static gint summary_cmp_by_simplified_subject
7876 	(GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2)
7877 {
7878 	const FolderItemPrefs *prefs;
7879 	const gchar *str1, *str2;
7880 	const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7881 	const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7882 	const MsgInfo *msginfo1 = r1->data;
7883 	const MsgInfo *msginfo2 = r2->data;
7884 	const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7885 	gint res;
7886 
7887 	cm_return_val_if_fail(sv, -1);
7888 	cm_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
7889 
7890 	if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) {
7891 		str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7892 		str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7893 	} else {
7894 		str1 = msginfo1->subject;
7895 		str2 = msginfo2->subject;
7896 	}
7897 
7898 	if (!str1)
7899 		return str2 != NULL;
7900 
7901 	if (!str2)
7902 		return -1;
7903 
7904 	prefs = msginfo1->folder->prefs;
7905 	if (!prefs)
7906 		prefs = msginfo2->folder->prefs;
7907 	if (!prefs)
7908 		return -1;
7909 
7910 	res = subject_compare_for_sort(str1, str2);
7911 	return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7912 }
7913 
summary_cmp_by_score(GtkCMCList * clist,gconstpointer ptr1,gconstpointer ptr2)7914 static gint summary_cmp_by_score(GtkCMCList *clist,
7915 				 gconstpointer ptr1, gconstpointer ptr2)
7916 {
7917 	MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7918 	MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7919 	int diff;
7920 
7921 	/* if score are equal, sort by date */
7922 
7923 	diff = msginfo1->score - msginfo2->score;
7924 	if (diff != 0)
7925 		return diff;
7926 	else
7927 		return summary_cmp_by_date(clist, ptr1, ptr2);
7928 }
7929 
summary_ignore_thread_func_mark_unread(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)7930 static void summary_ignore_thread_func_mark_unread(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7931 {
7932 	MsgInfo *msginfo;
7933 
7934 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7935 	cm_return_if_fail(msginfo);
7936 
7937 	summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7938 	summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
7939 
7940 	debug_print("Message %d is marked as ignore thread\n", msginfo->msgnum);
7941 }
7942 
summary_ignore_thread_func_set_row(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)7943 static void summary_ignore_thread_func_set_row(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7944 {
7945 	SummaryView *summaryview = (SummaryView *) data;
7946 	MsgInfo *msginfo;
7947 
7948 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7949 	cm_return_if_fail(msginfo);
7950 
7951 	summary_set_row_marks(summaryview, row);
7952 	debug_print("Message %d update in row view\n", msginfo->msgnum);
7953 }
7954 
summary_ignore_thread(SummaryView * summaryview)7955 void summary_ignore_thread(SummaryView *summaryview)
7956 {
7957 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7958 	GList *cur;
7959 	gboolean froze = FALSE;
7960 
7961 	START_LONG_OPERATION(summaryview, FALSE);
7962 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7963 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7964 					GTK_CMCTREE_FUNC(summary_ignore_thread_func_mark_unread),
7965 					summaryview);
7966 
7967 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7968 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7969 					GTK_CMCTREE_FUNC(summary_ignore_thread_func_set_row),
7970 					summaryview);
7971 
7972 	END_LONG_OPERATION(summaryview);
7973 
7974 	summary_status_show(summaryview);
7975 }
7976 
summary_unignore_thread_func(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)7977 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7978 {
7979 	SummaryView *summaryview = (SummaryView *) data;
7980 	MsgInfo *msginfo;
7981 
7982 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7983 	cm_return_if_fail(msginfo);
7984 
7985 	summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
7986 
7987 	summary_set_row_marks(summaryview, row);
7988 	debug_print("Message %d is marked as unignore thread\n",
7989 	    msginfo->msgnum);
7990 }
7991 
summary_unignore_thread(SummaryView * summaryview)7992 void summary_unignore_thread(SummaryView *summaryview)
7993 {
7994 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7995 	GList *cur;
7996 	gboolean froze = FALSE;
7997 
7998 	START_LONG_OPERATION(summaryview, FALSE);
7999 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8000 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8001 					GTK_CMCTREE_FUNC(summary_unignore_thread_func),
8002 					summaryview);
8003 
8004 	END_LONG_OPERATION(summaryview);
8005 
8006 	summary_status_show(summaryview);
8007 }
8008 
summary_check_ignore_thread_func(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)8009 static void summary_check_ignore_thread_func
8010 		(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8011 {
8012 	MsgInfo *msginfo;
8013 	gint *found_ignore = (gint *) data;
8014 
8015 	if (*found_ignore) return;
8016 	else {
8017 		msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8018 		*found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags);
8019 	}
8020 }
8021 
summary_toggle_ignore_thread(SummaryView * summaryview)8022 void summary_toggle_ignore_thread(SummaryView *summaryview)
8023 {
8024 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8025 	GList *cur;
8026 	gint found_ignore = 0;
8027 
8028 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8029 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8030 					GTK_CMCTREE_FUNC(summary_check_ignore_thread_func),
8031 					&found_ignore);
8032 
8033 	if (found_ignore)
8034 		summary_unignore_thread(summaryview);
8035 	else
8036 		summary_ignore_thread(summaryview);
8037 }
8038 
summary_watch_thread_func(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)8039 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8040 {
8041 	SummaryView *summaryview = (SummaryView *) data;
8042 	MsgInfo *msginfo;
8043 
8044 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8045 	cm_return_if_fail(msginfo);
8046 
8047 	summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
8048 
8049 	summary_set_row_marks(summaryview, row);
8050 	debug_print("Message %d is marked as watch thread\n",
8051 	    msginfo->msgnum);
8052 }
8053 
summary_watch_thread(SummaryView * summaryview)8054 void summary_watch_thread(SummaryView *summaryview)
8055 {
8056 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8057 	GList *cur;
8058 	gboolean froze = FALSE;
8059 
8060 	START_LONG_OPERATION(summaryview, FALSE);
8061 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8062 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8063 					GTK_CMCTREE_FUNC(summary_watch_thread_func),
8064 					summaryview);
8065 
8066 	END_LONG_OPERATION(summaryview);
8067 
8068 	summary_status_show(summaryview);
8069 }
8070 
summary_unwatch_thread_func(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)8071 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8072 {
8073 	SummaryView *summaryview = (SummaryView *) data;
8074 	MsgInfo *msginfo;
8075 
8076 	msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8077 	cm_return_if_fail(msginfo);
8078 
8079 	summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
8080 
8081 	summary_set_row_marks(summaryview, row);
8082 	debug_print("Message %d is marked as unwatch thread\n",
8083 	    msginfo->msgnum);
8084 }
8085 
summary_unwatch_thread(SummaryView * summaryview)8086 void summary_unwatch_thread(SummaryView *summaryview)
8087 {
8088 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8089 	GList *cur;
8090 	gboolean froze = FALSE;
8091 
8092 	START_LONG_OPERATION(summaryview, FALSE);
8093 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8094 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8095 					GTK_CMCTREE_FUNC(summary_unwatch_thread_func),
8096 					summaryview);
8097 
8098 	END_LONG_OPERATION(summaryview);
8099 
8100 	summary_status_show(summaryview);
8101 }
8102 
summary_check_watch_thread_func(GtkCMCTree * ctree,GtkCMCTreeNode * row,gpointer data)8103 static void summary_check_watch_thread_func
8104 		(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8105 {
8106 	MsgInfo *msginfo;
8107 	gint *found_watch = (gint *) data;
8108 
8109 	if (*found_watch) return;
8110 	else {
8111 		msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8112 		*found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
8113 	}
8114 }
8115 
summary_toggle_watch_thread(SummaryView * summaryview)8116 void summary_toggle_watch_thread(SummaryView *summaryview)
8117 {
8118 	GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8119 	GList *cur;
8120 	gint found_watch = 0;
8121 
8122 	for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8123 		gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8124 					GTK_CMCTREE_FUNC(summary_check_watch_thread_func),
8125 					&found_watch);
8126 
8127 	if (found_watch)
8128 		summary_unwatch_thread(summaryview);
8129 	else
8130 		summary_watch_thread(summaryview);
8131 }
8132 
8133 
summary_toggle_show_read_messages(SummaryView * summaryview)8134 void summary_toggle_show_read_messages(SummaryView *summaryview)
8135 {
8136 	FolderItemUpdateData source;
8137 	if (summaryview->folder_item->hide_read_msgs)
8138  		summaryview->folder_item->hide_read_msgs = 0;
8139  	else
8140  		summaryview->folder_item->hide_read_msgs = 1;
8141 
8142 	source.item = summaryview->folder_item;
8143 	source.update_flags = F_ITEM_UPDATE_NAME;
8144 	source.msg = NULL;
8145 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8146  	summary_show(summaryview, summaryview->folder_item, FALSE);
8147 }
8148 
summary_toggle_show_del_messages(SummaryView * summaryview)8149 void summary_toggle_show_del_messages(SummaryView *summaryview)
8150 {
8151 	FolderItemUpdateData source;
8152 	if (summaryview->folder_item->hide_del_msgs)
8153  		summaryview->folder_item->hide_del_msgs = 0;
8154  	else
8155  		summaryview->folder_item->hide_del_msgs = 1;
8156 
8157 	source.item = summaryview->folder_item;
8158 	source.update_flags = F_ITEM_UPDATE_NAME;
8159 	source.msg = NULL;
8160 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8161  	summary_show(summaryview, summaryview->folder_item, FALSE);
8162 }
8163 
summary_toggle_show_read_threads(SummaryView * summaryview)8164 void summary_toggle_show_read_threads(SummaryView *summaryview)
8165 {
8166 	FolderItemUpdateData source;
8167 	if (summaryview->folder_item->hide_read_threads)
8168  		summaryview->folder_item->hide_read_threads = 0;
8169  	else
8170  		summaryview->folder_item->hide_read_threads = 1;
8171 
8172 	source.item = summaryview->folder_item;
8173 	source.update_flags = F_ITEM_UPDATE_NAME;
8174 	source.msg = NULL;
8175 	hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8176  	summary_show(summaryview, summaryview->folder_item, FALSE);
8177 }
8178 
summary_set_hide_menu(SummaryView * summaryview,const gchar * menu_item,guint action)8179 static void summary_set_hide_menu (SummaryView *summaryview,
8180 				   const gchar *menu_item,
8181 				   guint action)
8182 {
8183  	GtkWidget *widget;
8184 
8185  	widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, menu_item);
8186 	cm_return_if_fail(widget != NULL);
8187 
8188  	g_object_set_data(G_OBJECT(widget), "dont_toggle",
8189  			  GINT_TO_POINTER(1));
8190  	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
8191  	g_object_set_data(G_OBJECT(widget), "dont_toggle",
8192  			  GINT_TO_POINTER(0));
8193 }
8194 
summary_reflect_prefs_pixmap_theme(SummaryView * summaryview)8195 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
8196 {
8197 	GtkWidget *pixmap;
8198 
8199 	stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
8200 	stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
8201 	stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
8202 	stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
8203 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
8204 	stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
8205 	stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
8206 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
8207 	stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
8208 	stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
8209 	stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
8210 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
8211 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
8212 	stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
8213 	stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
8214 	stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
8215 	stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
8216 	stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
8217 	stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
8218 
8219 	summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8220 
8221 	pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
8222 	gtk_container_remove (GTK_CONTAINER(summaryview->toggle_search),
8223 			      summaryview->quick_search_pixmap);
8224 	gtk_container_add(GTK_CONTAINER(summaryview->toggle_search), pixmap);
8225 	gtk_widget_show(pixmap);
8226 	summaryview->quick_search_pixmap = pixmap;
8227 
8228 #ifdef GENERIC_UMPC
8229 	pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
8230 	gtk_container_remove (GTK_CONTAINER(summaryview->multiple_sel_togbtn),
8231 			      summaryview->multiple_sel_image);
8232 	gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
8233 	gtk_widget_show(pixmap);
8234 	summaryview->multiple_sel_togbtn = pixmap;
8235 #endif
8236 
8237 	folderview_unselect(summaryview->folderview);
8238 	folderview_select(summaryview->folderview, summaryview->folder_item);
8239 	summary_set_column_titles(summaryview);
8240 }
8241 
summary_reflect_prefs_custom_colors(SummaryView * summaryview)8242 void summary_reflect_prefs_custom_colors(SummaryView *summaryview)
8243 {
8244 	GtkMenuShell *menu;
8245 	GList *children, *cur;
8246 
8247 	/* re-create colorlabel submenu */
8248 	menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
8249 	cm_return_if_fail(menu != NULL);
8250 
8251 	/* clear items. get item pointers. */
8252 	children = gtk_container_get_children(GTK_CONTAINER(menu));
8253 	for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
8254 		g_signal_handlers_disconnect_matched
8255 			 (gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager),
8256 			 G_SIGNAL_MATCH_DATA|G_SIGNAL_MATCH_FUNC,
8257 			 0, 0, NULL, mainwin_accel_changed_cb, cur->data);
8258 		gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
8259 	}
8260 	g_list_free(children);
8261 	summary_colorlabel_menu_create(summaryview, TRUE);
8262 }
8263 
8264 /*
8265  * Harvest addresses for selected messages in summary view.
8266  */
summary_harvest_address(SummaryView * summaryview)8267 void summary_harvest_address(SummaryView *summaryview)
8268 {
8269 	GtkCMCTree *ctree = GTK_CMCTREE( summaryview->ctree );
8270 	GList *cur;
8271 	GList *msgList;
8272 	MsgInfo *msginfo;
8273 
8274 	msgList = NULL;
8275 	for( cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next ) {
8276 		msginfo = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(cur->data) );
8277 		if (!msginfo)
8278 			continue;
8279 		msgList = g_list_append( msgList, GUINT_TO_POINTER( msginfo->msgnum ) );
8280 	}
8281 
8282 	addressbook_harvest( summaryview->folder_item, TRUE, msgList );
8283 
8284 	g_list_free( msgList );
8285 }
8286 
summary_compile_simplify_regexp(gchar * simplify_subject_regexp)8287 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
8288 {
8289 	int err;
8290 	gchar buf[BUFFSIZE];
8291 	regex_t *preg = NULL;
8292 
8293 	preg = g_new0(regex_t, 1);
8294 
8295 	err = string_match_precompile(simplify_subject_regexp,
8296 				      preg, REG_EXTENDED);
8297 	if (err) {
8298 		regerror(err, preg, buf, BUFFSIZE);
8299 		alertpanel_error(_("Regular expression (regexp) error:\n%s"), buf);
8300 		g_free(preg);
8301 		preg = NULL;
8302 	}
8303 
8304 	return preg;
8305 }
summary_set_prefs_from_folderitem(SummaryView * summaryview,FolderItem * item)8306 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
8307 {
8308 	FolderSortKey sort_key;
8309 	FolderSortType sort_type;
8310 	cm_return_if_fail(summaryview != NULL);
8311 	cm_return_if_fail(item != NULL);
8312 
8313 	/* Subject simplification */
8314 	if(summaryview->simplify_subject_preg) {
8315 		regfree(summaryview->simplify_subject_preg);
8316 		g_free(summaryview->simplify_subject_preg);
8317 		summaryview->simplify_subject_preg = NULL;
8318 	}
8319 	if(item->prefs && item->prefs->simplify_subject_regexp &&
8320 	   item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
8321 		summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
8322 
8323 	/* Sorting */
8324 	sort_key = item->sort_key;
8325 	sort_type = item->sort_type;
8326 
8327 	folder_get_sort_type(item->folder, &sort_key, &sort_type);
8328 
8329 	summaryview->sort_key = sort_key;
8330 	summaryview->sort_type = sort_type;
8331 
8332 	/* Threading */
8333 	summaryview->threaded = item->threaded;
8334 	summaryview->thread_collapsed = item->thread_collapsed;
8335 
8336 	/* Scoring */
8337 }
8338 
summary_save_prefs_to_folderitem(SummaryView * summaryview,FolderItem * item)8339 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
8340 {
8341 	/* Sorting */
8342 	item->sort_key = summaryview->sort_key;
8343 	item->sort_type = summaryview->sort_type;
8344 
8345 	/* Threading */
8346 	item->threaded = summaryview->threaded;
8347 	item->thread_collapsed = summaryview->thread_collapsed;
8348 }
8349 
summary_update_msg(gpointer source,gpointer data)8350 static gboolean summary_update_msg(gpointer source, gpointer data)
8351 {
8352 	MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
8353 	SummaryView *summaryview = (SummaryView *)data;
8354 	GtkCMCTreeNode *node;
8355 
8356 	cm_return_val_if_fail(msginfo_update != NULL, TRUE);
8357 	cm_return_val_if_fail(summaryview != NULL, FALSE);
8358 
8359 	if (msginfo_update->msginfo->folder != summaryview->folder_item)
8360 		return FALSE;
8361 
8362 	if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
8363 		node = gtk_cmctree_find_by_row_data(
8364 				GTK_CMCTREE(summaryview->ctree), NULL,
8365 				msginfo_update->msginfo);
8366 
8367 		if (node)
8368 			summary_set_row_marks(summaryview, node);
8369 	}
8370 
8371 	return FALSE;
8372 }
8373 
summary_update_unread(SummaryView * summaryview,FolderItem * removed_item)8374 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item)
8375 {
8376 	guint new, unread, unreadmarked, marked, total;
8377 	guint replied, forwarded, locked, ignored, watched;
8378 	static gboolean tips_initialized = FALSE;
8379 
8380 	if (prefs_common.layout_mode != SMALL_LAYOUT) {
8381 		if (tips_initialized) {
8382 			summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8383 			CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8384 			     NULL);
8385 			tips_initialized = FALSE;
8386 		}
8387 		return;
8388 	}
8389 	folder_count_total_msgs(&new, &unread, &unreadmarked, &marked, &total,
8390 				&replied, &forwarded, &locked, &ignored,
8391 				&watched);
8392 	if (removed_item) {
8393 		total -= removed_item->total_msgs;
8394 		new -= removed_item->new_msgs;
8395 		unread -= removed_item->unread_msgs;
8396 	}
8397 
8398 	if (new > 0 || unread > 0) {
8399 		tips_initialized = TRUE;
8400 		summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN_HRM);
8401 		CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8402 			     _("Go back to the folder list (You have unread messages)"));
8403 	} else {
8404 		tips_initialized = TRUE;
8405 		summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8406 		CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8407 			     _("Go back to the folder list"));
8408 	}
8409 }
8410 
summary_update_folder_item_hook(gpointer source,gpointer data)8411 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data)
8412 {
8413 	FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source;
8414 	SummaryView *summaryview = (SummaryView *)data;
8415 
8416 	cm_return_val_if_fail(hookdata != NULL, FALSE);
8417 	cm_return_val_if_fail(hookdata->item != NULL, FALSE);
8418 	cm_return_val_if_fail(summaryview != NULL, FALSE);
8419 
8420 	if (hookdata->item == summaryview->folder_item &&
8421 	    hookdata->update_flags & F_ITEM_UPDATE_NAME) {
8422 		gchar *name = folder_item_get_name(hookdata->item);
8423 		gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
8424 		g_free(name);
8425 	}
8426 	summary_update_unread(summaryview, NULL);
8427 
8428 	return FALSE;
8429 }
8430 
summary_update_folder_hook(gpointer source,gpointer data)8431 static gboolean summary_update_folder_hook(gpointer source, gpointer data)
8432 {
8433 	FolderUpdateData *hookdata;
8434 	SummaryView *summaryview = (SummaryView *)data;
8435 	hookdata = source;
8436 	if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
8437 		summary_update_unread(summaryview, hookdata->item);
8438 	} else
8439 		summary_update_unread(summaryview, NULL);
8440 
8441 	return FALSE;
8442 }
8443 
8444 /*!
8445  *\brief	change summaryview to display your answer(s) to a message
8446  *
8447  *\param	summaryview The SummaryView ;)
8448  *\param	msginfo The message for which answers are searched
8449  *
8450  */
summary_find_answers(SummaryView * summaryview,MsgInfo * msg)8451 static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
8452 {
8453 	FolderItem *sent_folder = NULL;
8454 	PrefsAccount *account = NULL;
8455 	GtkCMCTreeNode *node = NULL;
8456 	char *buf = NULL;
8457 	if (msg == NULL || msg->msgid == NULL)
8458 		return;
8459 
8460 	account = account_get_reply_account(msg, prefs_common.reply_account_autosel);
8461 	if (account == NULL)
8462 		return;
8463 	sent_folder = account_get_special_folder
8464 				(account, F_OUTBOX);
8465 
8466 	buf = g_strdup_printf("inreplyto matchcase \"%s\"", msg->msgid);
8467 
8468 	if (sent_folder != summaryview->folder_item) {
8469 		folderview_select(summaryview->mainwin->folderview, sent_folder);
8470 	}
8471 
8472 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
8473 
8474 	quicksearch_set(summaryview->quicksearch, ADVANCED_SEARCH_EXTENDED, buf);
8475 	g_free(buf);
8476 
8477 	node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
8478 	if (node)
8479 		summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
8480 }
8481 
summaryview_export_mbox_list(SummaryView * summaryview)8482 gint summaryview_export_mbox_list(SummaryView *summaryview)
8483 /* return values: -2 skipped, -1 error, 0 OK */
8484 {
8485 	GSList *list = summary_get_selected_msg_list(summaryview);
8486 	gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
8487 	gint ret;
8488 
8489 	if (mbox == NULL)
8490 		return -2;
8491 	if (list == NULL)
8492 		return -1;
8493 
8494 	ret = export_list_to_mbox(list, mbox);
8495 
8496 	g_slist_free(list);
8497 	g_free(mbox);
8498 
8499 	return ret;
8500 }
8501 
summaryview_lock(SummaryView * summaryview,FolderItem * item)8502 void summaryview_lock(SummaryView *summaryview, FolderItem *item)
8503 {
8504 	if (!summaryview || !summaryview->folder_item || !item) {
8505 		return;
8506 	}
8507 
8508 	if (summaryview->folder_item->folder == item->folder) {
8509 		gtk_widget_set_sensitive(summaryview->ctree, FALSE);
8510 	}
8511 }
summaryview_unlock(SummaryView * summaryview,FolderItem * item)8512 void summaryview_unlock(SummaryView *summaryview, FolderItem *item)
8513 {
8514 	gtk_widget_set_sensitive(summaryview->ctree, TRUE);
8515 }
8516 
summary_reedit_cb(GtkAction * gaction,gpointer data)8517 static void summary_reedit_cb(GtkAction *gaction, gpointer data)
8518 {
8519 	SummaryView *summaryview = (SummaryView *)data;
8520 	summary_reedit(summaryview);
8521 }
8522 
8523 #define DO_ACTION(name, act)	{ if (!strcmp(a_name, name)) action = act; }
summary_reply_cb(GtkAction * gaction,gpointer data)8524 static void summary_reply_cb(GtkAction *gaction, gpointer data)
8525 {
8526 	SummaryView *summaryview = (SummaryView *)data;
8527 	GSList *msginfo_list = NULL;
8528 	gint action = COMPOSE_REPLY;
8529 	const gchar *a_name = gtk_action_get_name(gaction);
8530 
8531 	DO_ACTION("SummaryViewPopup/Reply", COMPOSE_REPLY);
8532 	DO_ACTION("SummaryViewPopup/ReplyTo/All", COMPOSE_REPLY_TO_ALL);
8533 	DO_ACTION("SummaryViewPopup/ReplyTo/Sender", COMPOSE_REPLY_TO_SENDER);
8534 	DO_ACTION("SummaryViewPopup/ReplyTo/List", COMPOSE_REPLY_TO_LIST);
8535 	DO_ACTION("SummaryViewPopup/Forward", COMPOSE_FORWARD_INLINE);
8536 	DO_ACTION("SummaryViewPopup/ForwardAtt", COMPOSE_FORWARD_AS_ATTACH);
8537 	DO_ACTION("SummaryViewPopup/Redirect", COMPOSE_REDIRECT);
8538 
8539 	msginfo_list = summary_get_selection(summaryview);
8540 	cm_return_if_fail(msginfo_list != NULL);
8541 	compose_reply_from_messageview(NULL, msginfo_list, action);
8542 	g_slist_free(msginfo_list);
8543 }
8544 
summary_is_opened_message_selected(SummaryView * summaryview)8545 gboolean summary_is_opened_message_selected(SummaryView *summaryview)
8546 {
8547 	GList *sel = NULL;
8548 
8549 	cm_return_val_if_fail(summaryview != NULL, FALSE);
8550 
8551 	sel = GTK_CMCLIST(summaryview->ctree)->selection;
8552 
8553 	if (summaryview->displayed == NULL || sel == NULL) {
8554 		return FALSE;
8555 	}
8556 
8557 	for ( ; sel != NULL; sel = sel->next) {
8558 		if (summaryview->displayed == GTK_CMCTREE_NODE(sel->data)) {
8559 			return TRUE;
8560 		}
8561 	}
8562 	return FALSE;
8563 }
8564 
summary_has_opened_message(SummaryView * summaryview)8565 gboolean summary_has_opened_message(SummaryView *summaryview)
8566 {
8567 	cm_return_val_if_fail(summaryview != NULL, FALSE);
8568 
8569 	return (summaryview->displayed != NULL);
8570 }
8571 
summary_header_lock_sorting_cb(GtkAction * gaction,gpointer data)8572 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data)
8573 {
8574 	SummaryView *summaryview = (SummaryView *)data;
8575 	gboolean sorting_lock = prefs_common_get_prefs()->summary_col_lock;
8576 
8577 	if (summaryview->header_menu_lock)
8578 		return;
8579 
8580 	debug_print("%slocking summaryview columns\n",
8581 			sorting_lock ? "un" : "");
8582 	prefs_common_get_prefs()->summary_col_lock = !sorting_lock;
8583 }
8584 
summary_header_set_displayed_columns_cb(GtkAction * gaction,gpointer data)8585 static void summary_header_set_displayed_columns_cb(GtkAction *gaction,
8586 		gpointer data)
8587 {
8588 	SummaryView *summaryview = (SummaryView *)data;
8589 
8590 	if (summaryview->header_menu_lock)
8591 		return;
8592 
8593 	prefs_summary_column_open();
8594 }
8595