Improve some sieve-related translations
[claws.git] / src / summaryview.c
blobd39306a4f86474e79414650533508c124f5a267f
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "config.h"
20 #include "defs.h"
22 #include <glib.h>
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtk.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
32 #include "main.h"
33 #include "menu.h"
34 #include "mbox.h"
35 #include "mainwindow.h"
36 #include "folderview.h"
37 #include "summaryview.h"
38 #include "messageview.h"
39 #include "mimeview.h"
40 #include "foldersel.h"
41 #include "procmsg.h"
42 #include "procheader.h"
43 #include "sourcewindow.h"
44 #include "prefs_common.h"
45 #include "prefs_summary_column.h"
46 #include "prefs_summary_open.h"
47 #include "prefs_filtering.h"
48 #include "account.h"
49 #include "compose.h"
50 #include "file-utils.h"
51 #include "utils.h"
52 #include "gtkutils.h"
53 #include "stock_pixmap.h"
54 #include "filesel.h"
55 #include "alertpanel.h"
56 #include "inputdialog.h"
57 #include "statusbar.h"
58 #include "folder.h"
59 #include "colorlabel.h"
60 #include "inc.h"
61 #include "imap.h"
62 #ifndef USE_ALT_ADDRBOOK
63 #include "addressbook.h"
64 #else
65 #include "addressbook-dbus.h"
66 #include "addressadd.h"
67 #endif
68 #include "addr_compl.h"
69 #include "folder_item_prefs.h"
70 #include "filtering.h"
71 #include "string_match.h"
72 #include "toolbar.h"
73 #include "news.h"
74 #include "hooks.h"
75 #include "description_window.h"
76 #include "folderutils.h"
77 #include "quicksearch.h"
78 #include "partial_download.h"
79 #include "tags.h"
80 #include "timing.h"
81 #include "log.h"
82 #include "edittags.h"
83 #include "manual.h"
84 #include "manage_window.h"
85 #include "avatars.h"
87 #define SUMMARY_COL_MARK_WIDTH 10
88 #define SUMMARY_COL_STATUS_WIDTH 13
89 #define SUMMARY_COL_LOCKED_WIDTH 13
90 #define SUMMARY_COL_MIME_WIDTH 11
92 static int normal_row_height = -1;
93 static GtkStyle *bold_style;
95 static GdkPixbuf *markxpm;
96 static GdkPixbuf *deletedxpm;
97 static GdkPixbuf *movedxpm;
98 static GdkPixbuf *copiedxpm;
100 static GdkPixbuf *newxpm;
101 static GdkPixbuf *unreadxpm;
102 static GdkPixbuf *repliedxpm;
103 static GdkPixbuf *forwardedxpm;
104 static GdkPixbuf *repliedandforwardedxpm;
105 static GdkPixbuf *ignorethreadxpm;
106 static GdkPixbuf *watchthreadxpm;
107 static GdkPixbuf *lockedxpm;
108 static GdkPixbuf *spamxpm;
110 static GdkPixbuf *clipxpm;
111 static GdkPixbuf *keyxpm;
112 static GdkPixbuf *clipkeyxpm;
113 static GdkPixbuf *keysignxpm;
114 static GdkPixbuf *gpgsignedxpm;
115 static GdkPixbuf *clipgpgsignedxpm;
117 static void summary_free_msginfo_func (GtkCMCTree *ctree,
118 GtkCMCTreeNode *node,
119 gpointer data);
120 static void summary_set_marks_func (GtkCMCTree *ctree,
121 GtkCMCTreeNode *node,
122 gpointer data);
124 void summary_set_menu_sensitive (SummaryView *summaryview);
125 guint summary_get_msgnum (SummaryView *summaryview,
126 GtkCMCTreeNode *node);
129 static void summary_set_hide_menu (SummaryView *summaryview,
130 const gchar *menu_item,
131 guint action);
133 static GtkCMCTreeNode *summary_find_prev_msg
134 (SummaryView *summaryview,
135 GtkCMCTreeNode *current_node,
136 gboolean start_from_prev);
137 static GtkCMCTreeNode *summary_find_next_msg
138 (SummaryView *summaryview,
139 GtkCMCTreeNode *current_node,
140 gboolean start_from_next);
142 static GtkCMCTreeNode *summary_find_prev_flagged_msg
143 (SummaryView *summaryview,
144 GtkCMCTreeNode *current_node,
145 MsgPermFlags flags,
146 gboolean start_from_prev);
147 static GtkCMCTreeNode *summary_find_next_flagged_msg
148 (SummaryView *summaryview,
149 GtkCMCTreeNode *current_node,
150 MsgPermFlags flags,
151 gboolean start_from_next);
153 static GtkCMCTreeNode *summary_find_msg_by_msgnum
154 (SummaryView *summaryview,
155 guint msgnum);
157 static void summary_update_status (SummaryView *summaryview);
159 /* display functions */
160 static void summary_status_show (SummaryView *summaryview);
161 static void summary_set_column_titles (SummaryView *summaryview);
162 static void summary_set_ctree_from_list (SummaryView *summaryview,
163 GSList *mlist,
164 guint selected_msgnum);
165 static inline void summary_set_header (SummaryView *summaryview,
166 gchar *text[],
167 MsgInfo *msginfo);
168 static void summary_display_msg (SummaryView *summaryview,
169 GtkCMCTreeNode *row);
170 static void summary_display_msg_full (SummaryView *summaryview,
171 GtkCMCTreeNode *row,
172 gboolean new_window,
173 gboolean all_headers);
174 static void summary_set_row_marks (SummaryView *summaryview,
175 GtkCMCTreeNode *row);
177 static gboolean summary_set_row_tag (SummaryView *summaryview,
178 GtkCMCTreeNode *row,
179 gboolean refresh,
180 gboolean set,
181 gint id);
182 /* message handling */
183 static void summary_mark_row (SummaryView *summaryview,
184 GtkCMCTreeNode *row);
185 static void summary_lock_row (SummaryView *summaryview,
186 GtkCMCTreeNode *row);
187 static void summary_unlock_row (SummaryView *summaryview,
188 GtkCMCTreeNode *row);
189 static void summary_mark_row_as_read (SummaryView *summaryview,
190 GtkCMCTreeNode *row);
191 static void summary_mark_row_as_unread (SummaryView *summaryview,
192 GtkCMCTreeNode *row);
193 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed);
194 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed);
195 static void summary_delete_row (SummaryView *summaryview,
196 GtkCMCTreeNode *row);
197 static void summary_unmark_row (SummaryView *summaryview,
198 GtkCMCTreeNode *row);
199 static void summary_move_row_to (SummaryView *summaryview,
200 GtkCMCTreeNode *row,
201 FolderItem *to_folder);
202 static void summary_copy_row_to (SummaryView *summaryview,
203 GtkCMCTreeNode *row,
204 FolderItem *to_folder);
206 static gint summary_execute_move (SummaryView *summaryview);
207 static void summary_execute_move_func (GtkCMCTree *ctree,
208 GtkCMCTreeNode *node,
209 gpointer data);
210 static void summary_execute_copy (SummaryView *summaryview);
211 static void summary_execute_copy_func (GtkCMCTree *ctree,
212 GtkCMCTreeNode *node,
213 gpointer data);
214 static void summary_execute_delete (SummaryView *summaryview);
215 static void summary_execute_delete_func (GtkCMCTree *ctree,
216 GtkCMCTreeNode *node,
217 gpointer data);
218 static void summary_execute_expunge (SummaryView *summaryview);
220 static void summary_thread_init (SummaryView *summaryview);
222 static void summary_unthread_for_exec (SummaryView *summaryview);
223 static void summary_unthread_for_exec_func (GtkCMCTree *ctree,
224 GtkCMCTreeNode *node,
225 gpointer data);
227 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp,
228 GSList * mlist);
230 static void summary_filter_func (MsgInfo *msginfo,
231 PrefsAccount *ac_prefs);
233 static void summary_colorlabel_menu_item_activate_cb
234 (GtkWidget *widget,
235 gpointer data);
236 static void summary_colorlabel_menu_item_activate_item_cb
237 (GtkMenuItem *label_menu_item,
238 gpointer data);
239 static void summary_colorlabel_menu_create(SummaryView *summaryview,
240 gboolean refresh);
241 static void summary_tags_menu_item_activate_cb
242 (GtkWidget *widget,
243 gpointer data);
244 static void summary_tags_menu_item_activate_item_cb
245 (GtkMenuItem *label_menu_item,
246 gpointer data);
247 static void summary_tags_menu_create(SummaryView *summaryview,
248 gboolean refresh);
250 static GtkWidget *summary_ctree_create (SummaryView *summaryview);
252 /* callback functions */
253 static gint summary_toggle_pressed (GtkWidget *eventbox,
254 GdkEventButton *event,
255 SummaryView *summaryview);
256 #ifdef GENERIC_UMPC
257 static void summary_toggle_multiple_pressed
258 (GtkWidget *widget,
259 SummaryView *summaryview);
260 #endif
261 static gint summary_folder_eventbox_pressed
262 (GtkWidget *eventbox,
263 GdkEventButton *event,
264 SummaryView *summaryview);
265 static gboolean summary_button_pressed (GtkWidget *ctree,
266 GdkEventButton *event,
267 SummaryView *summaryview);
268 static gboolean summary_button_released (GtkWidget *ctree,
269 GdkEventButton *event,
270 SummaryView *summaryview);
271 static gboolean summary_key_pressed (GtkWidget *ctree,
272 GdkEventKey *event,
273 SummaryView *summaryview);
274 static void summary_tree_expanded (GtkCMCTree *ctree,
275 GtkCMCTreeNode *node,
276 SummaryView *summaryview);
277 static void summary_tree_collapsed (GtkCMCTree *ctree,
278 GtkCMCTreeNode *node,
279 SummaryView *summaryview);
280 static void summary_selected (GtkCMCTree *ctree,
281 GtkCMCTreeNode *row,
282 gint column,
283 SummaryView *summaryview);
284 static void summary_unselected (GtkCMCTree *ctree,
285 GtkCMCTreeNode *row,
286 gint column,
287 SummaryView *summaryview);
288 static void summary_col_resized (GtkCMCList *clist,
289 gint column,
290 gint width,
291 SummaryView *summaryview);
292 static void summary_mark_clicked (GtkWidget *button,
293 SummaryView *summaryview);
294 static void summary_status_clicked (GtkWidget *button,
295 SummaryView *summaryview);
296 static void summary_mime_clicked (GtkWidget *button,
297 SummaryView *summaryview);
298 static void summary_num_clicked (GtkWidget *button,
299 SummaryView *summaryview);
300 static void summary_score_clicked (GtkWidget *button,
301 SummaryView *summaryview);
302 static void summary_size_clicked (GtkWidget *button,
303 SummaryView *summaryview);
304 static void summary_date_clicked (GtkWidget *button,
305 SummaryView *summaryview);
306 static void summary_from_clicked (GtkWidget *button,
307 SummaryView *summaryview);
308 static void summary_to_clicked (GtkWidget *button,
309 SummaryView *summaryview);
310 static void summary_subject_clicked (GtkWidget *button,
311 SummaryView *summaryview);
312 static void summary_score_clicked (GtkWidget *button,
313 SummaryView *summaryview);
314 static void summary_locked_clicked (GtkWidget *button,
315 SummaryView *summaryview);
316 static void summary_tags_clicked (GtkWidget *button,
317 SummaryView *summaryview);
319 static void summary_start_drag (GtkWidget *widget,
320 int button,
321 GdkEvent *event,
322 SummaryView *summaryview);
323 static void summary_drag_data_get (GtkWidget *widget,
324 GdkDragContext *drag_context,
325 GtkSelectionData *selection_data,
326 guint info,
327 guint time,
328 SummaryView *summaryview);
329 static void summary_drag_data_received(GtkWidget *widget,
330 GdkDragContext *drag_context,
331 gint x,
332 gint y,
333 GtkSelectionData *data,
334 guint info,
335 guint time,
336 SummaryView *summaryview);
337 static gboolean summary_drag_motion_cb(GtkWidget *widget,
338 GdkDragContext *context,
339 gint x,
340 gint y,
341 guint time,
342 SummaryView *summaryview);
343 static void summary_drag_end(GtkWidget *widget,
344 GdkDragContext *drag_context,
345 SummaryView *summaryview);
347 /* custom compare functions for sorting */
348 static gint summary_cmp_by_mark (GtkCMCList *clist,
349 gconstpointer ptr1,
350 gconstpointer ptr2);
351 static gint summary_cmp_by_status (GtkCMCList *clist,
352 gconstpointer ptr1,
353 gconstpointer ptr2);
354 static gint summary_cmp_by_mime (GtkCMCList *clist,
355 gconstpointer ptr1,
356 gconstpointer ptr2);
357 static gint summary_cmp_by_num (GtkCMCList *clist,
358 gconstpointer ptr1,
359 gconstpointer ptr2);
360 static gint summary_cmp_by_size (GtkCMCList *clist,
361 gconstpointer ptr1,
362 gconstpointer ptr2);
363 static gint summary_cmp_by_date (GtkCMCList *clist,
364 gconstpointer ptr1,
365 gconstpointer ptr2);
366 static gint summary_cmp_by_thread_date (GtkCMCList *clist,
367 gconstpointer ptr1,
368 gconstpointer ptr2);
369 static gint summary_cmp_by_from (GtkCMCList *clist,
370 gconstpointer ptr1,
371 gconstpointer ptr2);
372 static gint summary_cmp_by_simplified_subject
373 (GtkCMCList *clist,
374 gconstpointer ptr1,
375 gconstpointer ptr2);
376 static gint summary_cmp_by_score (GtkCMCList *clist,
377 gconstpointer ptr1,
378 gconstpointer ptr2);
379 static gint summary_cmp_by_label (GtkCMCList *clist,
380 gconstpointer ptr1,
381 gconstpointer ptr2);
382 static gint summary_cmp_by_to (GtkCMCList *clist,
383 gconstpointer ptr1,
384 gconstpointer ptr2);
385 static gint summary_cmp_by_subject (GtkCMCList *clist,
386 gconstpointer ptr1,
387 gconstpointer ptr2);
388 static gint summary_cmp_by_locked (GtkCMCList *clist,
389 gconstpointer ptr1,
390 gconstpointer ptr2);
391 static gint summary_cmp_by_tags (GtkCMCList *clist,
392 gconstpointer ptr1,
393 gconstpointer ptr2);
395 static void quicksearch_execute_cb (QuickSearch *quicksearch,
396 gpointer data);
398 static void tog_searchbar_cb (GtkWidget *w,
399 gpointer data);
401 static void summary_find_answers (SummaryView *summaryview,
402 MsgInfo *msg);
404 static gboolean summary_update_msg (gpointer source, gpointer data);
405 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data);
406 static gboolean summary_update_folder_hook(gpointer source, gpointer data);
407 static void summary_set_colorlabel_color (GtkCMCTree *ctree,
408 GtkCMCTreeNode *node,
409 guint labelcolor);
410 static void summary_thread_build(SummaryView *summaryview);
412 GtkTargetEntry summary_drag_types[3] =
414 {"text/uri-list", 0, TARGET_MAIL_URI_LIST},
415 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
416 {"claws-mail/msg-path-list", 0, TARGET_MAIL_CM_PATH_LIST},
419 static void summary_reedit_cb(GtkAction *gaction, gpointer data);
420 static void summary_reply_cb(GtkAction *gaction, gpointer data);
422 /* Only submenus and specifically-handled menu entries here */
423 static GtkActionEntry summary_popup_entries[] =
425 {"SummaryViewPopup", NULL, "SummaryViewPopup", NULL, NULL, NULL },
426 {"SummaryViewPopup/Reedit", NULL, N_("Re-edit"), NULL, NULL, G_CALLBACK(summary_reedit_cb) },
427 {"SummaryViewPopup/Reply", NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY */
428 {"SummaryViewPopup/ReplyTo", NULL, N_("Repl_y to"), NULL, NULL, NULL },
429 {"SummaryViewPopup/ReplyTo/All", NULL, N_("_All"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_ALL */
430 {"SummaryViewPopup/ReplyTo/Sender", NULL, N_("_Sender"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_SENDER */
431 {"SummaryViewPopup/ReplyTo/List", NULL, N_("Mailing _list"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_LIST */
433 {"SummaryViewPopup/Forward", NULL, N_("_Forward"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_INLINE */
434 {"SummaryViewPopup/ForwardAtt", NULL, N_("For_ward as attachment"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_AS_ATTACH */
435 {"SummaryViewPopup/Redirect", NULL, N_("Redirec_t"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REDIRECT */
436 {"SummaryViewPopup/Mark", NULL, N_("_Mark"), NULL, NULL, NULL },
437 {"SummaryViewPopup/ColorLabel", NULL, N_("Color la_bel"), NULL, NULL, NULL },
438 {"SummaryViewPopup/Tags", NULL, N_("Ta_gs"), NULL, NULL, NULL },
439 {"SummaryViewPopup/CreateFilterRule", NULL, N_("Create _filter rule"), NULL, NULL, NULL },
440 #ifndef GENERIC_UMPC
441 {"SummaryViewPopup/CreateProcessingRule", NULL, N_("Create processing rule"), NULL, NULL, NULL },
442 #endif
443 {"SummaryViewPopup/View", NULL, N_("_View"), NULL, NULL, NULL },
446 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data);
447 static void summary_header_set_displayed_columns_cb(GtkAction *gaction, gpointer data);
449 static GtkActionEntry summary_header_popup_entries[] =
451 {"SummaryViewHeaderPopup", NULL, "SummaryViewHeaderPopup", NULL, NULL, NULL },
452 {"SummaryViewHeaderPopup/SetDisplayedColumns", NULL, N_("_Set displayed columns"), NULL, NULL, G_CALLBACK(summary_header_set_displayed_columns_cb) }
455 static GtkToggleActionEntry summary_header_popup_toggle_entries[] =
457 {"SummaryViewHeaderPopup/LockColumnHeaders", NULL, N_("_Lock column headers"), NULL, NULL, G_CALLBACK(summary_header_lock_sorting_cb), FALSE }
460 static const gchar *const col_label[N_SUMMARY_COLS] = {
461 "", /* S_COL_MARK */
462 N_("S"), /* S_COL_STATUS */
463 "", /* S_COL_MIME */
464 N_("Subject"), /* S_COL_SUBJECT */
465 N_("From"), /* S_COL_FROM */
466 N_("To"), /* S_COL_TO */
467 N_("Date"), /* S_COL_DATE */
468 N_("Size"), /* S_COL_SIZE */
469 N_("#"), /* S_COL_NUMBER */
470 N_("Score"), /* S_COL_SCORE */
471 "", /* S_COL_LOCKED */
472 N_("Tags"), /* S_COL_TAGS */
475 void summary_freeze(SummaryView *summaryview)
477 if (summaryview)
478 gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree));
481 void summary_thaw(SummaryView *summaryview)
483 if (summaryview)
484 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
487 void summary_thaw_with_status(SummaryView *summaryview)
489 if (summaryview) {
490 summary_status_show(summaryview);
491 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree));
495 void summary_grab_focus(SummaryView *summaryview)
497 if (summaryview)
498 gtk_widget_grab_focus(summaryview->ctree);
501 GtkWidget *summary_get_main_widget(SummaryView *summaryview)
503 if (summaryview)
504 return summaryview->ctree;
505 else
506 return NULL;
509 #define START_LONG_OPERATION(summaryview,force_freeze) { \
510 summary_lock(summaryview); \
511 main_window_cursor_wait(summaryview->mainwin); \
512 if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\
513 froze = TRUE; \
514 summary_freeze(summaryview); \
516 folder_item_update_freeze(); \
517 inc_lock(); \
518 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, \
519 summaryview->msginfo_update_callback_id); \
521 #define END_LONG_OPERATION(summaryview) { \
522 inc_unlock(); \
523 folder_item_update_thaw(); \
524 if (froze) \
525 summary_thaw(summaryview); \
526 main_window_cursor_normal(summaryview->mainwin); \
527 summary_unlock(summaryview); \
528 summaryview->msginfo_update_callback_id = \
529 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, \
530 summary_update_msg, (gpointer) summaryview); \
533 static void popup_menu_selection_done(GtkMenuShell *shell, gpointer user_data)
535 SummaryView *summaryview = (SummaryView *)user_data;
537 cm_return_if_fail(summaryview != NULL);
539 /* If a message is displayed, place cursor back on the message. */
540 if (summaryview->displayed != NULL &&
541 summaryview->displayed != summaryview->selected) {
542 gtk_sctree_select(GTK_SCTREE(summaryview->ctree), summaryview->displayed);
546 SummaryView *summary_create(MainWindow *mainwin)
548 SummaryView *summaryview;
549 GtkWidget *vbox;
550 GtkWidget *scrolledwin;
551 GtkWidget *ctree;
552 GtkWidget *hbox;
553 GtkWidget *hbox_l;
554 GtkWidget *stat_box;
555 GtkWidget *stat_box2;
556 GtkWidget *stat_vbox;
557 GtkWidget *statlabel_folder;
558 GtkWidget *statlabel_select;
559 GtkWidget *statlabel_msgs;
560 GtkWidget *hbox_spc;
561 GtkWidget *toggle_eventbox;
562 #ifdef GENERIC_UMPC
563 GtkWidget *multiple_sel_togbtn;
564 #endif
565 GtkWidget *toggle_arrow;
566 GtkWidget *toggle_search;
567 QuickSearch *quicksearch;
569 debug_print("Creating summary view...\n");
570 summaryview = g_new0(SummaryView, 1);
572 #define SUMMARY_VBOX_SPACING 3
573 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SUMMARY_VBOX_SPACING);
574 gtk_widget_set_name(GTK_WIDGET(vbox), "summaryview");
576 /* create status label */
577 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
578 gtk_widget_show(hbox);
580 stat_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
581 gtk_widget_show(stat_vbox);
583 stat_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
584 gtk_widget_show(stat_box);
586 stat_box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
587 gtk_widget_show(stat_box2);
589 toggle_search = gtk_toggle_button_new();
590 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_search),
591 prefs_common.show_searchbar);
592 gtk_widget_set_can_focus(toggle_search, FALSE);
593 gtk_widget_show(toggle_search);
595 CLAWS_SET_TIP(toggle_search, _("Toggle quick search bar"));
597 gtk_box_pack_start(GTK_BOX(hbox), toggle_search, FALSE, FALSE, 2);
599 gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0);
600 gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0);
601 gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0);
603 hbox_l = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
604 gtk_widget_show(hbox_l);
605 gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0);
607 statlabel_folder = gtk_label_new("");
608 gtk_widget_show(statlabel_folder);
609 gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2);
610 statlabel_select = gtk_label_new("");
611 gtk_widget_show(statlabel_select);
612 gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12);
614 /* toggle view button */
615 toggle_eventbox = gtk_event_box_new();
616 gtk_widget_show(toggle_eventbox);
618 gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, 4);
620 toggle_arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU);
621 gtk_widget_show(toggle_arrow);
622 gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow);
623 g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event",
624 G_CALLBACK(summary_toggle_pressed),
625 summaryview);
627 #ifdef GENERIC_UMPC
628 multiple_sel_togbtn = gtk_toggle_button_new();
629 gtk_widget_show(multiple_sel_togbtn);
630 gtk_box_pack_end(GTK_BOX(hbox), multiple_sel_togbtn, FALSE, FALSE, 4);
631 CLAWS_SET_TIP(multiple_sel_togbtn,
632 _("Toggle multiple selection"));
633 g_signal_connect(G_OBJECT(multiple_sel_togbtn), "toggled",
634 G_CALLBACK(summary_toggle_multiple_pressed),
635 summaryview);
636 #endif
638 statlabel_msgs = gtk_label_new("");
639 gtk_widget_show(statlabel_msgs);
640 gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, 4);
642 hbox_spc = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
643 gtk_widget_show(hbox_spc);
644 gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6);
646 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
647 gtk_widget_show(scrolledwin);
648 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
649 GTK_POLICY_AUTOMATIC,
650 GTK_POLICY_AUTOMATIC);
651 summaryview->mainwidget_book = gtk_notebook_new();
652 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
653 gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE);
654 #ifndef GENERIC_UMPC
655 gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
656 scrolledwin);
657 gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
658 #endif
660 ctree = summary_ctree_create(summaryview);
661 gtk_widget_show(ctree);
663 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
664 GTK_CMCLIST(ctree)->hadjustment);
665 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
666 GTK_CMCLIST(ctree)->vadjustment);
667 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
669 /* status label */
670 gtk_widget_show_all(stat_vbox);
671 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
673 /* quick search */
674 quicksearch = quicksearch_new();
675 gtk_box_pack_start(GTK_BOX(vbox), quicksearch_get_widget(quicksearch), FALSE, FALSE, 0);
677 #ifdef GENERIC_UMPC
678 gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book),
679 scrolledwin);
680 gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0);
681 #endif
682 quicksearch_set_execute_callback(quicksearch, quicksearch_execute_cb, summaryview);
684 g_signal_connect (G_OBJECT(toggle_search), "toggled",
685 G_CALLBACK(tog_searchbar_cb), summaryview);
687 /* create popup menu */
689 gtk_action_group_add_actions(mainwin->action_group,
690 summary_popup_entries,
691 G_N_ELEMENTS(summary_popup_entries),
692 (gpointer)summaryview);
694 gtk_action_group_add_actions(mainwin->action_group,
695 summary_header_popup_entries,
696 G_N_ELEMENTS(summary_header_popup_entries),
697 (gpointer)summaryview);
699 gtk_action_group_add_toggle_actions(mainwin->action_group,
700 summary_header_popup_toggle_entries,
701 G_N_ELEMENTS(summary_header_popup_toggle_entries),
702 (gpointer)summaryview);
704 summaryview->ui_manager = gtk_ui_manager_new();
705 summaryview->action_group = cm_menu_create_action_group_full(summaryview->ui_manager,"Menu", summary_popup_entries,
706 G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview);
708 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU)
709 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reedit", "SummaryViewPopup/Reedit", GTK_UI_MANAGER_MENUITEM)
710 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reply", "SummaryViewPopup/Reply", GTK_UI_MANAGER_MENUITEM)
711 #ifndef GENERIC_UMPC
712 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU)
713 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR)
714 #endif
715 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Forward", "SummaryViewPopup/Forward", GTK_UI_MANAGER_MENUITEM)
716 #ifndef GENERIC_UMPC
717 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ForwardAtt", "SummaryViewPopup/ForwardAtt", GTK_UI_MANAGER_MENUITEM)
718 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Redirect", "SummaryViewPopup/Redirect", GTK_UI_MANAGER_MENUITEM)
719 #endif
720 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR)
721 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Move", "Message/Move", GTK_UI_MANAGER_MENUITEM)
722 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Copy", "Message/Copy", GTK_UI_MANAGER_MENUITEM)
723 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Trash", "Message/Trash", GTK_UI_MANAGER_MENUITEM)
724 #ifndef GENERIC_UMPC
725 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Delete", "Message/Delete", GTK_UI_MANAGER_MENUITEM)
726 #endif
727 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR)
728 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Mark", "SummaryViewPopup/Mark", GTK_UI_MANAGER_MENU)
729 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ColorLabel", "SummaryViewPopup/ColorLabel", GTK_UI_MANAGER_MENU)
730 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Tags", "SummaryViewPopup/Tags", GTK_UI_MANAGER_MENU)
732 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR)
733 #ifndef GENERIC_UMPC
734 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "AddSenderToAB", "Tools/AddSenderToAB", GTK_UI_MANAGER_MENUITEM)
735 #endif
736 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateFilterRule", "SummaryViewPopup/CreateFilterRule", GTK_UI_MANAGER_MENU)
737 #ifndef GENERIC_UMPC
738 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "CreateProcessingRule", "SummaryViewPopup/CreateProcessingRule", GTK_UI_MANAGER_MENU)
739 #endif
740 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator5", "Tools/---", GTK_UI_MANAGER_SEPARATOR)
741 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU)
742 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "SaveAs", "File/SaveAs", GTK_UI_MANAGER_MENUITEM)
743 #ifndef GENERIC_UMPC
744 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Print", "File/Print", GTK_UI_MANAGER_MENUITEM)
745 #endif
746 /* last separator, for plugins */
747 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator6", "File/---", GTK_UI_MANAGER_SEPARATOR)
749 /* submenus - replyto */
750 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "All", "SummaryViewPopup/ReplyTo/All", GTK_UI_MANAGER_MENUITEM)
751 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "Sender", "SummaryViewPopup/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM)
752 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "MailingList", "SummaryViewPopup/ReplyTo/List", GTK_UI_MANAGER_MENUITEM)
754 /* submenus - mark */
755 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Mark", "Message/Mark/Mark", GTK_UI_MANAGER_MENUITEM)
756 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unmark", "Message/Mark/Unmark", GTK_UI_MANAGER_MENUITEM)
757 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator1", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
758 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkRead", "Message/Mark/MarkRead", GTK_UI_MANAGER_MENUITEM)
759 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkUnread", "Message/Mark/MarkUnread", GTK_UI_MANAGER_MENUITEM)
760 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator2", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
761 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "IgnoreThread", "Message/Mark/IgnoreThread", GTK_UI_MANAGER_MENUITEM)
762 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnignoreThread", "Message/Mark/UnignoreThread", GTK_UI_MANAGER_MENUITEM)
763 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "WatchThread", "Message/Mark/WatchThread", GTK_UI_MANAGER_MENUITEM)
764 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "UnwatchThread", "Message/Mark/UnwatchThread", GTK_UI_MANAGER_MENUITEM)
765 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator3", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
766 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkSpam", "Message/Mark/MarkSpam", GTK_UI_MANAGER_MENUITEM)
767 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "MarkHam", "Message/Mark/MarkHam", GTK_UI_MANAGER_MENUITEM)
768 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Separator4", "Message/Mark/---", GTK_UI_MANAGER_SEPARATOR)
769 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Lock", "Message/Mark/Lock", GTK_UI_MANAGER_MENUITEM)
770 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Mark", "Unlock", "Message/Mark/Unlock", GTK_UI_MANAGER_MENUITEM)
772 /* submenus - colorlabel and tags are dynamic */
773 /* submenus - createfilterrule */
774 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "Automatically", "Tools/CreateFilterRule/Automatically", GTK_UI_MANAGER_MENUITEM)
775 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByFrom", "Tools/CreateFilterRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
776 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "ByTo", "Tools/CreateFilterRule/ByTo", GTK_UI_MANAGER_MENUITEM)
777 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "BySubject", "Tools/CreateFilterRule/BySubject", GTK_UI_MANAGER_MENUITEM)
778 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateFilterRule", "BySender", "Tools/CreateFilterRule/BySender", GTK_UI_MANAGER_MENUITEM)
780 #ifndef GENERIC_UMPC
781 /* submenus - createprocessingrule */
782 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "Automatically", "Tools/CreateProcessingRule/Automatically", GTK_UI_MANAGER_MENUITEM)
783 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByFrom", "Tools/CreateProcessingRule/ByFrom", GTK_UI_MANAGER_MENUITEM)
784 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "ByTo", "Tools/CreateProcessingRule/ByTo", GTK_UI_MANAGER_MENUITEM)
785 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "BySubject", "Tools/CreateProcessingRule/BySubject", GTK_UI_MANAGER_MENUITEM)
786 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/CreateProcessingRule", "BySender", "Tools/CreateProcessingRule/BySender", GTK_UI_MANAGER_MENUITEM)
787 #endif
789 /* submenus - view */
790 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "OpenNewWindow", "View/OpenNewWindow", GTK_UI_MANAGER_MENUITEM)
791 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "MessageSource", "View/MessageSource", GTK_UI_MANAGER_MENUITEM)
792 #ifndef GENERIC_UMPC
793 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "AllHeaders", "View/AllHeaders", GTK_UI_MANAGER_MENUITEM)
794 #endif
796 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewHeaderPopup", "SummaryViewHeaderPopup", GTK_UI_MANAGER_MENU)
797 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "LockColumnHeaders", "SummaryViewHeaderPopup/LockColumnHeaders", GTK_UI_MANAGER_MENUITEM)
798 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "SetDisplayedColumns", "SummaryViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
800 summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
801 gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewPopup")) );
802 summaryview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
803 gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup")) );
805 summaryview->vbox = vbox;
806 summaryview->scrolledwin = scrolledwin;
807 summaryview->ctree = ctree;
808 summaryview->hbox = hbox;
809 summaryview->hbox_l = hbox_l;
810 summaryview->hbox_spc = hbox_spc;
811 summaryview->stat_box = stat_box;
812 summaryview->stat_box2 = stat_box2;
813 summaryview->statlabel_folder = statlabel_folder;
814 summaryview->statlabel_select = statlabel_select;
815 summaryview->statlabel_msgs = statlabel_msgs;
816 summaryview->toggle_eventbox = toggle_eventbox;
817 summaryview->toggle_arrow = toggle_arrow;
818 #ifdef GENERIC_UMPC
819 summaryview->multiple_sel_togbtn = multiple_sel_togbtn;
820 #endif
821 summaryview->toggle_search = toggle_search;
822 summaryview->lock_count = 0;
823 summaryview->msginfo_update_callback_id =
824 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview);
825 summaryview->folder_item_update_callback_id =
826 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST,
827 summary_update_folder_item_hook,
828 (gpointer) summaryview);
829 summaryview->folder_update_callback_id =
830 hooks_register_hook(FOLDER_UPDATE_HOOKLIST,
831 summary_update_folder_hook,
832 (gpointer) summaryview);
834 summaryview->target_list = gtk_target_list_new(summary_drag_types, 3);
836 summaryview->quicksearch = quicksearch;
838 /* CLAWS: need this to get the SummaryView * from
839 * the CList */
840 g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
842 gtk_widget_show_all(vbox);
844 gtk_widget_show(vbox);
846 if (prefs_common.show_searchbar)
847 quicksearch_show(quicksearch);
848 else
849 quicksearch_hide(quicksearch);
851 if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
852 prefs_common.layout_mode == SMALL_LAYOUT)
853 gtk_widget_hide(summaryview->toggle_eventbox);
855 return summaryview;
858 void summary_relayout(SummaryView *summaryview)
860 gtk_widget_realize(summaryview->stat_box);
862 g_object_ref(summaryview->hbox_l);
863 g_object_ref(summaryview->statlabel_msgs);
865 gtk_container_remove(GTK_CONTAINER(
866 gtk_widget_get_parent(summaryview->hbox_l)), summaryview->hbox_l);
867 gtk_container_remove(GTK_CONTAINER(
868 gtk_widget_get_parent(summaryview->statlabel_msgs)), summaryview->statlabel_msgs);
870 switch (prefs_common.layout_mode) {
871 case NORMAL_LAYOUT:
872 case WIDE_LAYOUT:
873 case WIDE_MSGLIST_LAYOUT:
874 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
875 gtk_box_pack_end(GTK_BOX(summaryview->stat_box), summaryview->statlabel_msgs, FALSE, FALSE, 4);
876 gtk_widget_show_all(summaryview->stat_box);
877 gtk_widget_show_all(summaryview->stat_box2);
878 if (prefs_common.layout_mode == WIDE_MSGLIST_LAYOUT ||
879 prefs_common.layout_mode == SMALL_LAYOUT)
880 gtk_widget_hide(summaryview->toggle_eventbox);
881 else
882 gtk_widget_show(summaryview->toggle_eventbox);
883 break;
884 case VERTICAL_LAYOUT:
885 case SMALL_LAYOUT:
886 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), summaryview->hbox_l, TRUE, TRUE, 0);
887 gtk_box_pack_start(GTK_BOX(summaryview->stat_box2), summaryview->statlabel_msgs, FALSE, FALSE, 4);
888 gtk_widget_show_all(summaryview->stat_box);
889 gtk_widget_show_all(summaryview->stat_box2);
890 if (prefs_common.layout_mode == SMALL_LAYOUT) {
891 gtk_widget_hide(summaryview->toggle_eventbox);
892 gtk_widget_hide(summaryview->statlabel_msgs);
893 } else {
894 gtk_widget_show(summaryview->toggle_eventbox);
895 gtk_widget_show(summaryview->statlabel_msgs);
898 break;
900 summary_set_column_order(summaryview);
902 g_object_unref(summaryview->hbox_l);
903 g_object_unref(summaryview->statlabel_msgs);
904 quicksearch_relayout(summaryview->quicksearch);
905 if (prefs_common.show_searchbar)
906 quicksearch_show(summaryview->quicksearch);
907 else
908 quicksearch_hide(summaryview->quicksearch);
911 static void summary_set_fonts(SummaryView *summaryview)
913 PangoFontDescription *font_desc;
914 gint size;
916 font_desc = pango_font_description_from_string(NORMAL_FONT);
917 if (font_desc) {
918 gtk_widget_override_font(summaryview->ctree, font_desc);
919 pango_font_description_free(font_desc);
922 if (!bold_style) {
923 bold_style = gtk_style_copy
924 (gtk_widget_get_style(summaryview->ctree));
926 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
927 font_desc = pango_font_description_from_string(NORMAL_FONT);
928 if (font_desc) {
929 pango_font_description_free(bold_style->font_desc);
930 bold_style->font_desc = font_desc;
932 pango_font_description_set_weight
933 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
934 } else {
935 font_desc = pango_font_description_from_string(BOLD_FONT);
936 if (font_desc) {
937 pango_font_description_free(bold_style->font_desc);
938 bold_style->font_desc = font_desc;
943 if (prefs_common.derive_from_normal_font || !SMALL_FONT) {
944 font_desc = pango_font_description_new();
945 size = pango_font_description_get_size
946 (gtk_widget_get_style(summaryview->ctree)->font_desc);
947 pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
948 } else {
949 font_desc = pango_font_description_from_string(SMALL_FONT);
951 if (font_desc) {
952 gtk_widget_override_font(summaryview->statlabel_folder, font_desc);
953 gtk_widget_override_font(summaryview->statlabel_select, font_desc);
954 gtk_widget_override_font(summaryview->statlabel_msgs, font_desc);
955 pango_font_description_free(font_desc);
960 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon)
962 GtkWidget *pixmap;
963 if (!summaryview->folder_pixmap_eventbox) {
964 summaryview->folder_pixmap_eventbox = gtk_event_box_new();
965 gtk_widget_show(summaryview->folder_pixmap_eventbox);
966 gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4);
967 gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */
968 g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event",
969 G_CALLBACK(summary_folder_eventbox_pressed),
970 summaryview);
972 if (summaryview->folder_pixmap)
973 gtk_widget_destroy(summaryview->folder_pixmap);
975 pixmap = stock_pixmap_widget(icon);
976 gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap);
977 gtk_widget_show(pixmap);
978 summaryview->folder_pixmap = pixmap;
981 void summary_init(SummaryView *summaryview)
983 GtkWidget *pixmap;
985 gtk_widget_realize(summaryview->ctree);
986 stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
987 stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
988 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
989 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
990 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
991 stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
992 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
993 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
994 stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
995 stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
996 stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
997 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
998 stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
999 stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
1000 stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
1001 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
1002 stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
1003 stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
1004 stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
1006 summary_set_fonts(summaryview);
1008 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
1010 pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
1011 gtk_container_add (GTK_CONTAINER(summaryview->toggle_search), pixmap);
1012 gtk_widget_show(pixmap);
1013 summaryview->quick_search_pixmap = pixmap;
1015 #ifdef GENERIC_UMPC
1016 pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
1017 gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
1018 gtk_widget_show(pixmap);
1019 summaryview->multiple_sel_image = pixmap;
1020 #endif
1022 /* Init summaryview prefs */
1023 summaryview->sort_key = SORT_BY_NONE;
1024 summaryview->sort_type = SORT_ASCENDING;
1026 /* Init summaryview extra data */
1027 summaryview->simplify_subject_preg = NULL;
1028 summary_clear_list(summaryview);
1029 summary_set_column_titles(summaryview);
1030 summary_colorlabel_menu_create(summaryview, FALSE);
1031 summary_tags_menu_create(summaryview, FALSE);
1032 main_create_mailing_list_menu (summaryview->mainwin, NULL);
1033 summary_set_menu_sensitive(summaryview);
1035 summaryview->header_menu_lock = FALSE;
1038 #define CURRENTLY_DISPLAYED(m) \
1039 ( (m->msgnum == displayed_msgnum) \
1040 && (!g_ascii_strcasecmp(m->folder->name,item->name)) )
1042 #define FOLDER_SHOWS_TO_HDR(i) \
1043 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \
1044 || folder_has_parent_of_type(i, F_DRAFT) \
1045 || folder_has_parent_of_type(i, F_QUEUE)) )
1047 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item)
1049 gboolean show_from = FALSE, show_to = FALSE;
1050 gboolean showing_from = FALSE, showing_to = FALSE;
1051 gint from_pos = 0, to_pos = 0;
1052 SummaryColumnState *col_state = summaryview->col_state;
1053 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1055 if (!item || ((prefs_common.layout_mode == VERTICAL_LAYOUT || prefs_common.layout_mode == SMALL_LAYOUT) && prefs_common.two_line_vert) )
1056 return;
1057 if (FOLDER_SHOWS_TO_HDR(item))
1058 show_to = TRUE;
1059 else
1060 show_from = TRUE;
1062 from_pos = summaryview->col_pos[S_COL_FROM];
1063 to_pos = summaryview->col_pos[S_COL_TO];
1064 showing_from = col_state[from_pos].visible;
1065 showing_to = col_state[to_pos].visible;
1067 if (showing_from && showing_to) {
1068 debug_print("showing both\n");
1069 return;
1072 if (!showing_from && !showing_to) {
1073 debug_print("showing none\n");
1074 return;
1077 debug_print("showing %s %s, must show %s %s\n",
1078 showing_from?"From":"",
1079 showing_to?"To":"",
1080 show_from?"From":"",
1081 show_to?"To":"");
1083 if (showing_from == show_from && showing_to == show_to)
1084 return;
1085 /* else we'll switch both */
1087 debug_print("switching columns\n");
1088 col_state[from_pos].type = S_COL_TO;
1089 col_state[from_pos].visible = show_to;
1091 col_state[to_pos].type = S_COL_FROM;
1092 col_state[to_pos].visible = show_from;
1094 summaryview->col_pos[S_COL_TO] = from_pos;
1095 summaryview->col_pos[S_COL_FROM] = to_pos;
1097 gtk_cmclist_set_column_visibility
1098 (GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible);
1099 gtk_cmclist_set_column_visibility
1100 (GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible);
1102 summary_set_column_titles(summaryview);
1105 static void summaryview_reset_recursive_folder_match(SummaryView *summaryview)
1107 GSList *cur;
1109 for (cur = summaryview->recursive_matched_folders; cur != NULL; cur = cur->next) {
1110 folderview_update_search_icon(cur->data, FALSE);
1113 g_slist_free(summaryview->recursive_matched_folders);
1114 summaryview->recursive_matched_folders = NULL;
1115 summaryview->search_root_folder = NULL;
1118 static gboolean summaryview_quicksearch_recursive_progress(gpointer data, guint at, guint matched, guint total)
1120 QuickSearch *search = (QuickSearch*) data;
1121 gint interval = quicksearch_is_fast(search) ? 5000 : 100;
1123 statusbar_progress_all(at, total, interval);
1124 if (at % interval == 0)
1125 GTK_EVENTS_FLUSH();
1127 if (matched > 0)
1128 return FALSE;
1130 return TRUE;
1133 static void summaryview_quicksearch_recurse_step(SummaryView *summaryview, FolderItem *item)
1135 MsgInfoList *msgs = NULL;
1136 gboolean result = TRUE;
1138 statusbar_print_all(_("Searching in %s... \n"),
1139 item->path ? item->path : "(null)");
1140 folder_item_update_freeze();
1142 quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_quicksearch_recursive_progress, summaryview->quicksearch);
1143 if (!quicksearch_run_on_folder(summaryview->quicksearch, item, &msgs))
1144 result = FALSE;
1146 result = result && msgs != NULL;
1148 if (msgs != NULL)
1149 procmsg_msg_list_free(msgs);
1151 folder_item_update_thaw();
1152 statusbar_progress_all(0, 0, 0);
1153 statusbar_pop_all();
1155 if (result) {
1156 summaryview->recursive_matched_folders = g_slist_prepend(
1157 summaryview->recursive_matched_folders, item);
1159 folderview_update_search_icon(item, TRUE);
1163 static void summaryview_quicksearch_search_subfolders(SummaryView *summaryview, FolderItem *folder_item)
1165 FolderItem *cur = NULL;
1166 GNode *node = folder_item->node->children;
1168 if (!prefs_common.summary_quicksearch_recurse
1169 || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1170 || quicksearch_is_in_typing(summaryview->quicksearch))
1171 return;
1173 for (; node != NULL; node = node->next) {
1174 if (!quicksearch_has_sat_predicate(summaryview->quicksearch))
1175 return;
1177 cur = FOLDER_ITEM(node->data);
1178 summaryview_quicksearch_recurse_step(summaryview, cur);
1179 if (cur->node->children)
1180 summaryview_quicksearch_search_subfolders(summaryview, cur);
1184 static void summaryview_quicksearch_recurse(SummaryView *summaryview)
1186 if (!prefs_common.summary_quicksearch_recurse
1187 || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1188 || summaryview->folder_item == NULL) {
1189 return;
1191 START_TIMING("");
1192 main_window_cursor_wait(summaryview->mainwin);
1194 summaryview_reset_recursive_folder_match(summaryview);
1195 summaryview->search_root_folder = summaryview->folder_item;
1197 summaryview_quicksearch_search_subfolders(summaryview, summaryview->folder_item);
1199 main_window_cursor_normal(summaryview->mainwin);
1200 END_TIMING();
1203 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist)
1205 int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0;
1206 GSList *cur;
1207 START_TIMING("");
1208 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1209 MsgInfo * msginfo = (MsgInfo *) cur->data;
1210 t++;
1211 if (MSG_IS_NEW(msginfo->flags))
1212 n++;
1213 if (MSG_IS_UNREAD(msginfo->flags))
1214 u++;
1215 if (MSG_IS_MARKED(msginfo->flags))
1216 m++;
1217 if (MSG_IS_REPLIED(msginfo->flags))
1218 r++;
1219 if (MSG_IS_FORWARDED(msginfo->flags))
1220 f++;
1221 if (MSG_IS_LOCKED(msginfo->flags))
1222 l++;
1223 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
1224 i++;
1225 if (MSG_IS_WATCH_THREAD(msginfo->flags))
1226 w++;
1228 if (t != item->total_msgs
1229 || n != item->new_msgs
1230 || u != item->unread_msgs
1231 || m != item->marked_msgs
1232 || r != item->replied_msgs
1233 || f != item->forwarded_msgs
1234 || l != item->locked_msgs
1235 || i != item->ignored_msgs
1236 || w != item->watched_msgs
1237 || (m == 0 && item->unreadmarked_msgs != 0)
1238 || item->unreadmarked_msgs < 0) {
1239 debug_print("Inconsistency\n");
1240 folder_item_scan_full(item, FALSE);
1241 END_TIMING();
1242 return FALSE;
1244 END_TIMING();
1245 return TRUE;
1248 gboolean summaryview_search_root_progress(gpointer data, guint at, guint matched, guint total)
1250 SummaryView *summaryview = (SummaryView*) data;
1252 gint interval = quicksearch_is_fast(summaryview->quicksearch) ? 5000 : 100;
1254 statusbar_progress_all(at, total, interval);
1256 if (at % interval == 0)
1257 GTK_EVENTS_FLUSH();
1259 return TRUE;
1262 gboolean summary_show(SummaryView *summaryview, FolderItem *item, gboolean avoid_refresh)
1264 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1265 GtkCMCTreeNode *node = NULL;
1266 GSList *mlist = NULL;
1267 gchar *buf;
1268 gboolean is_refresh;
1269 guint selected_msgnum = 0;
1270 guint displayed_msgnum = 0;
1271 GSList *cur;
1272 GSList *not_killed;
1273 gboolean hidden_removed = FALSE;
1275 if (summary_is_locked(summaryview)) return FALSE;
1277 if (!summaryview->mainwin)
1278 return FALSE;
1279 START_TIMING("");
1280 summary_switch_from_to(summaryview, item);
1282 inc_lock();
1283 summary_lock(summaryview);
1285 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
1287 utils_free_regex();
1289 is_refresh = (item == summaryview->folder_item && !avoid_refresh) ? TRUE : FALSE;
1291 if (item && item->folder->klass->item_opened) {
1292 item->folder->klass->item_opened(item);
1295 if (!is_refresh) {
1296 main_create_mailing_list_menu (summaryview->mainwin, NULL);
1297 if (prefs_common.layout_mode == SMALL_LAYOUT) {
1298 if (item) {
1299 mainwindow_enter_folder(summaryview->mainwin);
1300 gtk_widget_grab_focus(summaryview->ctree);
1304 if (!prefs_common.summary_quicksearch_sticky
1305 && (!prefs_common.summary_quicksearch_recurse
1306 || !quicksearch_has_sat_predicate(summaryview->quicksearch)
1307 || (item && !folder_is_child_of(item, summaryview->search_root_folder)))
1308 && !quicksearch_is_running(summaryview->quicksearch)
1309 && !is_refresh) {
1310 quicksearch_set(summaryview->quicksearch, prefs_common.summary_quicksearch_type, "");
1313 /* STATUSBAR_POP(summaryview->mainwin); */
1315 if (is_refresh) {
1316 selected_msgnum = summary_get_msgnum(summaryview,
1317 summaryview->selected);
1318 displayed_msgnum = summary_get_msgnum(summaryview,
1319 summaryview->displayed);
1322 /* process the marks if any */
1323 if (!is_refresh &&
1324 (summaryview->mainwin->lock_count == 0 &&
1325 (summaryview->moved > 0 || summaryview->copied > 0))) {
1326 AlertValue val;
1327 gboolean changed = FALSE;
1329 val = alertpanel(_("Process mark"),
1330 _("Some marks are left. Process them?"),
1331 NULL, _("_No"), NULL, _("_Yes"), NULL, _("_Cancel"),
1332 ALERTFOCUS_FIRST);
1333 if (G_ALERTALTERNATE == val) {
1334 summary_unlock(summaryview);
1335 summary_execute(summaryview);
1336 summary_lock(summaryview);
1337 changed = TRUE;
1338 } else if (G_ALERTDEFAULT == val) {
1339 /* DO NOTHING */
1340 } else {
1341 summary_unlock(summaryview);
1342 inc_unlock();
1343 END_TIMING();
1344 return FALSE;
1346 if (changed || !quicksearch_has_sat_predicate(summaryview->quicksearch))
1347 folder_update_op_count();
1350 summary_freeze(summaryview);
1352 summary_clear_list(summaryview);
1354 buf = NULL;
1355 if (!item || !item->path || !folder_item_parent(item) || item->no_select) {
1356 g_free(buf);
1357 debug_print("empty folder (%p %s %p %d)\n",
1358 item,
1359 (item && item->path)?item->path:"(null)",
1360 item?folder_item_parent(item):0x0,
1361 item?item->no_select:FALSE);
1362 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE);
1363 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE);
1364 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE);
1365 summary_clear_all(summaryview);
1366 summaryview->folder_item = item;
1367 summary_thaw(summaryview);
1368 summary_unlock(summaryview);
1369 inc_unlock();
1370 END_TIMING();
1371 return TRUE;
1373 g_free(buf);
1375 if (!is_refresh)
1376 messageview_clear(summaryview->messageview);
1378 summaryview->folder_item = item;
1379 item->opened = TRUE;
1381 buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path);
1382 debug_print("%s\n", buf);
1383 STATUSBAR_PUSH(summaryview->mainwin, buf);
1384 g_free(buf);
1386 main_window_cursor_wait(summaryview->mainwin);
1388 mlist = folder_item_get_msg_list(item);
1390 if (!summary_check_consistency(item, mlist)) {
1391 debug_print("reloading due to inconsistency\n");
1392 procmsg_msg_list_free(mlist);
1393 mlist = folder_item_get_msg_list(item);
1396 if (quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1397 procmsg_msg_list_free(mlist);
1398 mlist = NULL;
1400 START_TIMING("quicksearch");
1402 statusbar_print_all(_("Searching in %s... \n"),
1403 summaryview->folder_item->path ?
1404 summaryview->folder_item->path : "(null)");
1406 folder_item_update_freeze();
1408 quicksearch_set_on_progress_cb(summaryview->quicksearch, summaryview_search_root_progress, summaryview);
1409 quicksearch_run_on_folder(summaryview->quicksearch, summaryview->folder_item, &mlist);
1411 folder_item_update_thaw();
1412 statusbar_progress_all(0, 0, 0);
1413 statusbar_pop_all();
1415 if (!quicksearch_has_sat_predicate(summaryview->quicksearch)) {
1416 debug_print("search cancelled!\n");
1417 summary_thaw(summaryview);
1418 STATUSBAR_POP(summaryview->mainwin);
1419 main_window_cursor_normal(summaryview->mainwin);
1420 summary_unlock(summaryview);
1421 inc_unlock();
1422 summary_show(summaryview, summaryview->folder_item, FALSE);
1423 END_TIMING();
1424 return FALSE;
1426 END_TIMING();
1429 if ((summaryview->folder_item->hide_read_msgs
1430 || summaryview->folder_item->hide_del_msgs
1431 || summaryview->folder_item->hide_read_threads) &&
1432 quicksearch_has_sat_predicate(summaryview->quicksearch) == FALSE) {
1433 GSList *not_killed;
1435 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1436 summaryview->folder_item->hide_read_msgs);
1437 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1438 summaryview->folder_item->hide_del_msgs);
1439 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1440 summaryview->folder_item->hide_read_threads);
1441 not_killed = NULL;
1442 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1443 MsgInfo * msginfo = (MsgInfo *) cur->data;
1445 if (!msginfo->hidden) {
1446 if (MSG_IS_DELETED(msginfo->flags) && summaryview->folder_item->hide_del_msgs) {
1447 procmsg_msginfo_free(&msginfo);
1448 continue;
1450 if (summaryview->folder_item->hide_read_msgs) {
1451 if (MSG_IS_UNREAD(msginfo->flags) &&
1452 !MSG_IS_IGNORE_THREAD(msginfo->flags))
1453 not_killed = g_slist_prepend(not_killed, msginfo);
1454 else if (MSG_IS_MARKED(msginfo->flags) ||
1455 MSG_IS_LOCKED(msginfo->flags))
1456 not_killed = g_slist_prepend(not_killed, msginfo);
1457 else if (is_refresh &&
1458 (msginfo->msgnum == selected_msgnum ||
1459 msginfo->msgnum == displayed_msgnum))
1460 not_killed = g_slist_prepend(not_killed, msginfo);
1461 else
1462 procmsg_msginfo_free(&msginfo);
1463 } else {
1464 not_killed = g_slist_prepend(not_killed, msginfo);
1466 } else
1467 procmsg_msginfo_free(&msginfo);
1469 hidden_removed = TRUE;
1470 g_slist_free(mlist);
1471 mlist = not_killed;
1472 } else {
1473 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages",
1474 FALSE);
1475 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages",
1476 FALSE);
1477 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads",
1478 FALSE);
1481 if (!hidden_removed) {
1482 START_TIMING("removing hidden");
1483 not_killed = NULL;
1484 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) {
1485 MsgInfo * msginfo = (MsgInfo *) cur->data;
1487 if (!msginfo->hidden)
1488 not_killed = g_slist_prepend(not_killed, msginfo);
1489 else
1490 procmsg_msginfo_free(&msginfo);
1492 g_slist_free(mlist);
1493 mlist = not_killed;
1494 END_TIMING();
1497 STATUSBAR_POP(summaryview->mainwin);
1499 /* set ctree and hash table from the msginfo list, and
1500 create the thread */
1501 summary_set_ctree_from_list(summaryview, mlist, selected_msgnum);
1503 g_slist_free(mlist);
1505 if (is_refresh) {
1506 if (!quicksearch_is_in_typing(summaryview->quicksearch)) {
1507 summaryview->displayed =
1508 summary_find_msg_by_msgnum(summaryview,
1509 displayed_msgnum);
1510 if (!summaryview->displayed)
1511 messageview_clear(summaryview->messageview);
1512 summary_unlock(summaryview);
1514 if (quicksearch_is_running(summaryview->quicksearch))
1515 summary_select_by_msgnum(summaryview, selected_msgnum,
1516 OPEN_SELECTED_ON_SEARCH_RESULTS);
1517 else
1518 summary_select_by_msgnum(summaryview, selected_msgnum,
1519 FALSE);
1521 summary_lock(summaryview);
1522 if (!summaryview->selected) {
1523 /* no selected message - select first unread
1524 message, but do not display it */
1525 node = summary_find_next_flagged_msg(summaryview, NULL,
1526 MSG_UNREAD, FALSE);
1527 if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL)
1528 node = gtk_cmctree_node_nth
1529 (ctree,
1530 item->sort_type == SORT_DESCENDING
1531 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1532 summary_unlock(summaryview);
1534 if (quicksearch_is_running(summaryview->quicksearch))
1535 summary_select_node(summaryview, node,
1536 OPEN_SELECTED_ON_SEARCH_RESULTS);
1537 else
1538 summary_select_node(summaryview, node,
1539 OPEN_SELECTED_ON_FOLDER_OPEN);
1541 summary_lock(summaryview);
1543 } else {
1544 /* just select first/last */
1545 if (GTK_CMCLIST(ctree)->row_list != NULL)
1546 node = gtk_cmctree_node_nth
1547 (ctree,
1548 item->sort_type == SORT_DESCENDING
1549 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1550 summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
1552 } else {
1553 /* backward compat */
1554 int i = 0;
1555 gboolean set = FALSE, stop = FALSE;
1556 for (i = 0; i < 6; i++) {
1557 EntryAction act = prefs_common.summary_select_prio[i];
1559 if (act != ACTION_UNSET) {
1560 set = TRUE;
1561 break;
1564 if (!set)
1565 prefs_summary_open_set_defaults();
1567 for (i = 0; i < 6 && node == NULL; i++) {
1568 EntryAction act = prefs_common.summary_select_prio[i];
1570 switch(act) {
1571 case ACTION_OLDEST_MARKED:
1572 if (summaryview->sort_type == SORT_ASCENDING)
1573 node = summary_find_next_flagged_msg(summaryview, NULL,
1574 MSG_MARKED, FALSE);
1575 else
1576 node = summary_find_prev_flagged_msg(summaryview, NULL,
1577 MSG_MARKED, FALSE);
1578 break;
1579 case ACTION_NEWEST_MARKED:
1580 if (summaryview->sort_type == SORT_ASCENDING)
1581 node = summary_find_prev_flagged_msg(summaryview, NULL,
1582 MSG_MARKED, FALSE);
1583 else
1584 node = summary_find_next_flagged_msg(summaryview, NULL,
1585 MSG_MARKED, FALSE);
1586 break;
1587 case ACTION_OLDEST_NEW:
1588 if (summaryview->sort_type == SORT_ASCENDING)
1589 node = summary_find_next_flagged_msg(summaryview, NULL,
1590 MSG_NEW, FALSE);
1591 else
1592 node = summary_find_prev_flagged_msg(summaryview, NULL,
1593 MSG_NEW, FALSE);
1594 break;
1595 case ACTION_NEWEST_NEW:
1596 if (summaryview->sort_type == SORT_ASCENDING)
1597 node = summary_find_prev_flagged_msg(summaryview, NULL,
1598 MSG_NEW, FALSE);
1599 else
1600 node = summary_find_next_flagged_msg(summaryview, NULL,
1601 MSG_NEW, FALSE);
1602 break;
1603 case ACTION_OLDEST_UNREAD:
1604 if (summaryview->sort_type == SORT_ASCENDING)
1605 node = summary_find_next_flagged_msg(summaryview, NULL,
1606 MSG_UNREAD, FALSE);
1607 else
1608 node = summary_find_prev_flagged_msg(summaryview, NULL,
1609 MSG_UNREAD, FALSE);
1610 break;
1611 case ACTION_NEWEST_UNREAD:
1612 if (summaryview->sort_type == SORT_ASCENDING)
1613 node = summary_find_prev_flagged_msg(summaryview, NULL,
1614 MSG_UNREAD, FALSE);
1615 else
1616 node = summary_find_next_flagged_msg(summaryview, NULL,
1617 MSG_UNREAD, FALSE);
1618 break;
1619 case ACTION_LAST_OPENED:
1620 if (summaryview->folder_item) {
1621 node = summary_find_msg_by_msgnum(summaryview,
1622 summaryview->folder_item->last_seen);
1624 break;
1625 case ACTION_NEWEST_LIST:
1626 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1627 node = gtk_cmctree_node_nth
1628 (ctree,
1629 item->sort_type == SORT_DESCENDING
1630 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1632 break;
1633 case ACTION_OLDEST_LIST:
1634 if (GTK_CMCLIST(ctree)->row_list != NULL) {
1635 node = gtk_cmctree_node_nth
1636 (ctree,
1637 item->sort_type == SORT_ASCENDING
1638 ? 0 : GTK_CMCLIST(ctree)->rows - 1);
1640 break;
1641 case ACTION_NOTHING:
1642 case ACTION_UNSET:
1643 node = NULL;
1644 stop = TRUE;
1645 break;
1648 if (stop || node)
1649 break;
1652 summary_unlock(summaryview);
1654 if (node)
1655 summary_select_node(summaryview, node,
1656 OPEN_SELECTED_ON_FOLDER_OPEN);
1658 summary_lock(summaryview);
1661 summary_status_show(summaryview);
1662 summary_set_menu_sensitive(summaryview);
1663 toolbar_main_set_sensitive(summaryview->mainwin);
1665 summary_thaw(summaryview);
1666 debug_print("\n");
1667 STATUSBAR_PUSH(summaryview->mainwin, _("Done."));
1668 STATUSBAR_POP(summaryview->mainwin);
1669 main_window_cursor_normal(summaryview->mainwin);
1670 summary_unlock(summaryview);
1671 inc_unlock();
1672 END_TIMING();
1673 return TRUE;
1676 #undef CURRENTLY_DISPLAYED
1678 static void summary_cancel_mark_read_timeout(SummaryView *summaryview) {
1679 if (summaryview->mark_as_read_timeout_tag != 0) {
1680 g_source_remove(summaryview->mark_as_read_timeout_tag);
1681 summaryview->mark_as_read_timeout_tag = 0;
1685 void summary_clear_list(SummaryView *summaryview)
1687 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1688 gint optimal_width;
1690 summary_freeze(summaryview);
1692 gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree),
1693 NULL, summary_free_msginfo_func, NULL);
1695 if (summaryview->folder_item) {
1696 summaryview->folder_item->opened = FALSE;
1697 summaryview->folder_item = NULL;
1700 summary_cancel_mark_read_timeout(summaryview);
1702 summaryview->display_msg = FALSE;
1704 summaryview->selected = NULL;
1705 summaryview->displayed = NULL;
1706 summaryview->total_size = 0;
1707 summaryview->deleted = summaryview->moved = 0;
1708 summaryview->copied = 0;
1709 if (summaryview->msgid_table) {
1710 g_hash_table_destroy(summaryview->msgid_table);
1711 summaryview->msgid_table = NULL;
1713 if (summaryview->subject_table) {
1714 g_hash_table_destroy(summaryview->subject_table);
1715 summaryview->subject_table = NULL;
1717 summaryview->mlist = NULL;
1719 gtk_cmclist_clear(clist);
1720 if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
1721 optimal_width = gtk_cmclist_optimal_column_width
1722 (clist, summaryview->col_pos[S_COL_SUBJECT]);
1723 gtk_cmclist_set_column_width
1724 (clist, summaryview->col_pos[S_COL_SUBJECT],
1725 optimal_width);
1728 summary_thaw(summaryview);
1731 void summary_clear_all(SummaryView *summaryview)
1733 mimeview_clear(summaryview->messageview->mimeview);
1734 messageview_clear(summaryview->messageview);
1735 summary_clear_list(summaryview);
1736 summary_set_menu_sensitive(summaryview);
1737 toolbar_main_set_sensitive(summaryview->mainwin);
1738 summary_status_show(summaryview);
1741 void summary_lock(SummaryView *summaryview)
1743 summaryview->lock_count++;
1746 void summary_unlock(SummaryView *summaryview)
1748 if (summaryview->lock_count)
1749 summaryview->lock_count--;
1752 gboolean summary_is_locked(SummaryView *summaryview)
1754 return summaryview->lock_count > 0;
1757 SummarySelection summary_get_selection_type(SummaryView *summaryview)
1759 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
1760 SummarySelection selection;
1762 if (!clist->row_list)
1763 selection = SUMMARY_NONE;
1764 else if (!clist->selection)
1765 selection = SUMMARY_SELECTED_NONE;
1766 else if (!clist->selection->next)
1767 selection = SUMMARY_SELECTED_SINGLE;
1768 else
1769 selection = SUMMARY_SELECTED_MULTIPLE;
1771 return selection;
1775 *\return MsgInfo * Selected message if there's one selected;
1776 * if multiple selected, or none, return NULL.
1778 MsgInfo *summary_get_selected_msg(SummaryView *summaryview)
1780 /* summaryview->selected may be valid when multiple
1781 * messages were selected */
1782 GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection;
1784 if (sellist == NULL || sellist->next)
1785 return NULL;
1787 return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data);
1790 GSList *summary_get_selected_msg_list(SummaryView *summaryview)
1792 GSList *mlist = NULL;
1793 GList *cur;
1794 MsgInfo *msginfo;
1796 for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL;
1797 cur = cur->next) {
1798 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data);
1799 mlist = g_slist_prepend(mlist,
1800 procmsg_msginfo_new_ref(msginfo));
1803 mlist = g_slist_reverse(mlist);
1805 return mlist;
1808 void summary_set_menu_sensitive(SummaryView *summaryview)
1810 SensitiveCondMask state;
1812 main_window_set_menu_sensitive(summaryview->mainwin);
1814 state = main_window_get_current_state(summaryview->mainwin);
1816 #define SET_SENSITIVE(entry_str, ...) \
1818 SensitiveCondMask cond = main_window_get_mask(__VA_ARGS__, -1); \
1819 cm_menu_set_sensitive_full(summaryview->mainwin->ui_manager, \
1820 (const gchar *) entry_str, \
1821 ((cond & state) == cond)); \
1823 SET_SENSITIVE("Menus/SummaryViewPopup/Reedit", M_ALLOW_REEDIT);
1824 SET_SENSITIVE("Menus/SummaryViewPopup/Reply", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1825 #ifndef GENERIC_UMPC
1826 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1827 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/All", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1828 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/Sender", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1829 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/MailingList", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1830 #endif
1832 SET_SENSITIVE("Menus/SummaryViewPopup/Forward", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1833 #ifndef GENERIC_UMPC
1834 SET_SENSITIVE("Menus/SummaryViewPopup/ForwardAtt", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1835 SET_SENSITIVE("Menus/SummaryViewPopup/Redirect", M_HAVE_ACCOUNT, M_TARGET_EXIST);
1836 #endif
1838 SET_SENSITIVE("Menus/SummaryViewPopup/Move", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS);
1839 SET_SENSITIVE("Menus/SummaryViewPopup/Copy", M_TARGET_EXIST, M_EXEC);
1840 SET_SENSITIVE("Menus/SummaryViewPopup/Trash", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS, M_NOT_TRASH);
1841 #ifndef GENERIC_UMPC
1842 SET_SENSITIVE("Menus/SummaryViewPopup/Delete", M_TARGET_EXIST, M_ALLOW_DELETE);
1843 #endif
1845 SET_SENSITIVE("Menus/SummaryViewPopup/Mark", M_TARGET_EXIST);
1846 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Mark", M_TARGET_EXIST);
1847 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Unmark", M_TARGET_EXIST);
1848 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkRead", M_TARGET_EXIST);
1849 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkUnread", M_TARGET_EXIST);
1850 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/IgnoreThread", M_TARGET_EXIST);
1851 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/UnignoreThread", M_TARGET_EXIST);
1852 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/WatchThread", M_TARGET_EXIST);
1853 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/UnwatchThread", M_TARGET_EXIST);
1854 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Lock", M_TARGET_EXIST);
1855 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/Unlock", M_TARGET_EXIST);
1856 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkSpam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1857 SET_SENSITIVE("Menus/SummaryViewPopup/Mark/MarkHam", M_TARGET_EXIST, M_CAN_LEARN_SPAM);
1858 SET_SENSITIVE("Menus/SummaryViewPopup/ColorLabel", M_TARGET_EXIST);
1859 SET_SENSITIVE("Menus/SummaryViewPopup/Tags", M_TARGET_EXIST);
1861 #ifndef GENERIC_UMPC
1862 SET_SENSITIVE("Menus/SummaryViewPopup/AddSenderToAB", M_SINGLE_TARGET_EXIST);
1863 #endif
1864 SET_SENSITIVE("Menus/SummaryViewPopup/CreateFilterRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1865 #ifndef GENERIC_UMPC
1866 SET_SENSITIVE("Menus/SummaryViewPopup/CreateProcessingRule", M_SINGLE_TARGET_EXIST, M_UNLOCKED);
1867 #endif
1869 SET_SENSITIVE("Menus/SummaryViewPopup/View", M_SINGLE_TARGET_EXIST);
1870 SET_SENSITIVE("Menus/SummaryViewPopup/View/OpenNewWindow", M_SINGLE_TARGET_EXIST);
1871 SET_SENSITIVE("Menus/SummaryViewPopup/View/MessageSource", M_SINGLE_TARGET_EXIST);
1872 #ifndef GENERIC_UMPC
1873 SET_SENSITIVE("Menus/SummaryViewPopup/View/AllHeaders", M_SINGLE_TARGET_EXIST);
1874 #endif
1875 SET_SENSITIVE("Menus/SummaryViewPopup/SaveAs", M_TARGET_EXIST);
1876 #ifndef GENERIC_UMPC
1877 SET_SENSITIVE("Menus/SummaryViewPopup/Print", M_TARGET_EXIST);
1878 #endif
1879 #undef SET_SENSITIVE
1881 summary_lock(summaryview);
1882 #ifndef GENERIC_UMPC
1883 if (summaryview->messageview
1884 && summaryview->messageview->mimeview
1885 && summaryview->messageview->mimeview->textview)
1886 cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, "Menus/SummaryViewPopup/View/AllHeaders",
1887 prefs_common.show_all_headers);
1888 #endif
1889 summary_unlock(summaryview);
1891 void summary_select_prev(SummaryView *summaryview)
1893 GtkCMCTreeNode *node = summaryview->selected;
1894 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1896 if (summaryview->sort_type == SORT_ASCENDING)
1897 node = gtkut_ctree_node_prev(ctree, node);
1898 else
1899 node = gtkut_ctree_node_next(ctree, node);
1901 if (node && node != summaryview->selected)
1902 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1905 void summary_select_next(SummaryView *summaryview)
1907 GtkCMCTreeNode *node = summaryview->selected;
1908 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
1910 if (summaryview->sort_type == SORT_ASCENDING)
1911 node = gtkut_ctree_node_next(ctree, node);
1912 else
1913 node = gtkut_ctree_node_prev(ctree, node);
1915 if (node && node != summaryview->selected)
1916 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1919 void summary_select_prev_unread(SummaryView *summaryview)
1921 GtkCMCTreeNode *node;
1923 if (summaryview->sort_type == SORT_ASCENDING)
1924 node = summary_find_prev_flagged_msg
1925 (summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1926 else
1927 node = summary_find_next_flagged_msg
1928 (summaryview, summaryview->selected, MSG_UNREAD, TRUE);
1930 if (!node || node == summaryview->selected) {
1931 AlertValue val = 0;
1933 switch (prefs_common.next_unread_msg_dialog) {
1934 case NEXTUNREADMSGDIALOG_ALWAYS:
1935 val = alertpanel(_("No more unread messages"),
1936 _("No unread message found. "
1937 "Search from the end?"),
1938 NULL, _("_No"), NULL, _("_Yes"),
1939 NULL, NULL, ALERTFOCUS_SECOND);
1940 break;
1941 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1942 val = G_ALERTALTERNATE;
1943 break;
1944 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1945 val = !G_ALERTALTERNATE;
1946 break;
1947 default:
1948 debug_print(
1949 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
1951 if (val != G_ALERTALTERNATE) return;
1952 if (summaryview->sort_type == SORT_ASCENDING)
1953 node = summary_find_prev_flagged_msg(summaryview, NULL,
1954 MSG_UNREAD, FALSE);
1955 else
1956 node = summary_find_next_flagged_msg(summaryview, NULL,
1957 MSG_UNREAD, FALSE);
1960 if (!node)
1961 alertpanel_notice(_("No unread messages."));
1962 else
1963 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1966 void summary_select_next_unread(SummaryView *summaryview)
1968 GtkCMCTreeNode *node = summaryview->selected;
1970 if (summaryview->sort_type == SORT_ASCENDING)
1971 node = summary_find_next_flagged_msg
1972 (summaryview, node, MSG_UNREAD, TRUE);
1973 else
1974 node = summary_find_prev_flagged_msg
1975 (summaryview, node, MSG_UNREAD, TRUE);
1977 if (node)
1978 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
1979 else {
1980 AlertValue val = 0;
1982 switch (prefs_common.next_unread_msg_dialog) {
1983 case NEXTUNREADMSGDIALOG_ALWAYS:
1984 val = alertpanel(_("No more unread messages"),
1985 _("No unread message found. "
1986 "Go to next folder?"),
1987 NULL, _("_No"), NULL, _("_Yes"),
1988 NULL, NULL, ALERTFOCUS_SECOND);
1989 break;
1990 case NEXTUNREADMSGDIALOG_ASSUME_YES:
1991 val = G_ALERTALTERNATE;
1992 break;
1993 case NEXTUNREADMSGDIALOG_ASSUME_NO:
1994 val = G_ALERTOTHER;
1995 break;
1996 default:
1997 debug_print(
1998 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2001 if (val == G_ALERTALTERNATE)
2002 folderview_select_next_with_flag(summaryview->folderview, MSG_UNREAD);
2006 void summary_select_prev_new(SummaryView *summaryview)
2008 GtkCMCTreeNode *node;
2010 if (summaryview->sort_type == SORT_ASCENDING)
2011 node = summary_find_prev_flagged_msg
2012 (summaryview, summaryview->selected, MSG_NEW, TRUE);
2013 else
2014 node = summary_find_next_flagged_msg
2015 (summaryview, summaryview->selected, MSG_NEW, TRUE);
2017 if (!node || node == summaryview->selected) {
2018 AlertValue val = 0;
2020 switch (prefs_common.next_unread_msg_dialog) {
2021 case NEXTUNREADMSGDIALOG_ALWAYS:
2022 val = alertpanel(_("No more new messages"),
2023 _("No new message found. "
2024 "Search from the end?"),
2025 NULL, _("_No"), NULL, _("_Yes"),
2026 NULL, NULL, ALERTFOCUS_SECOND);
2027 break;
2028 case NEXTUNREADMSGDIALOG_ASSUME_YES:
2029 val = G_ALERTALTERNATE;
2030 break;
2031 case NEXTUNREADMSGDIALOG_ASSUME_NO:
2032 val = !G_ALERTALTERNATE;
2033 break;
2034 default:
2035 debug_print(
2036 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2038 if (val != G_ALERTALTERNATE) return;
2039 if (summaryview->sort_type == SORT_ASCENDING)
2040 node = summary_find_prev_flagged_msg(summaryview, NULL,
2041 MSG_NEW, FALSE);
2042 else
2043 node = summary_find_next_flagged_msg(summaryview, NULL,
2044 MSG_NEW, FALSE);
2047 if (!node)
2048 alertpanel_notice(_("No new messages."));
2049 else
2050 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2053 void summary_select_next_new(SummaryView *summaryview)
2055 GtkCMCTreeNode *node = summaryview->selected;
2057 if (summaryview->sort_type == SORT_ASCENDING)
2058 node = summary_find_next_flagged_msg
2059 (summaryview, node, MSG_NEW, TRUE);
2060 else
2061 node = summary_find_prev_flagged_msg
2062 (summaryview, node, MSG_NEW, TRUE);
2064 if (node)
2065 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2066 else {
2067 AlertValue val = 0;
2069 switch (prefs_common.next_unread_msg_dialog) {
2070 case NEXTUNREADMSGDIALOG_ALWAYS:
2071 val = alertpanel(_("No more new messages"),
2072 _("No new message found. "
2073 "Go to next folder?"),
2074 NULL, _("_No"), NULL, _("_Yes"),
2075 NULL, NULL, ALERTFOCUS_SECOND);
2076 break;
2077 case NEXTUNREADMSGDIALOG_ASSUME_YES:
2078 val = G_ALERTALTERNATE;
2079 break;
2080 case NEXTUNREADMSGDIALOG_ASSUME_NO:
2081 val = G_ALERTOTHER;
2082 break;
2083 default:
2084 debug_print(
2085 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2087 if (val == G_ALERTALTERNATE)
2088 folderview_select_next_with_flag(summaryview->folderview, MSG_NEW);
2092 void summary_select_prev_marked(SummaryView *summaryview)
2094 GtkCMCTreeNode *node;
2096 if (summaryview->sort_type == SORT_ASCENDING)
2097 node = summary_find_prev_flagged_msg
2098 (summaryview, summaryview->selected, MSG_MARKED, TRUE);
2099 else
2100 node = summary_find_next_flagged_msg
2101 (summaryview, summaryview->selected, MSG_MARKED, TRUE);
2103 if (!node) {
2104 AlertValue val;
2106 val = alertpanel(_("No more marked messages"),
2107 _("No marked message found. "
2108 "Search from the end?"),
2109 NULL, _("_No"), NULL, _("_Yes"),
2110 NULL, NULL, ALERTFOCUS_SECOND);
2111 if (val != G_ALERTALTERNATE) return;
2112 node = summary_find_prev_flagged_msg(summaryview, NULL,
2113 MSG_MARKED, TRUE);
2116 if (!node)
2117 alertpanel_notice(_("No marked messages."));
2118 else
2119 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2122 void summary_select_next_marked(SummaryView *summaryview)
2124 GtkCMCTreeNode *node = summaryview->selected;
2126 if (summaryview->sort_type == SORT_ASCENDING)
2127 node = summary_find_next_flagged_msg
2128 (summaryview, node, MSG_MARKED, TRUE);
2129 else
2130 node = summary_find_prev_flagged_msg
2131 (summaryview, node, MSG_MARKED, TRUE);
2133 if (node)
2134 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2135 else {
2136 AlertValue val = 0;
2138 switch (prefs_common.next_unread_msg_dialog) {
2139 case NEXTUNREADMSGDIALOG_ALWAYS:
2140 val = alertpanel(_("No more marked messages"),
2141 _("No marked message found. "
2142 "Go to next folder?"),
2143 NULL, _("_No"), NULL, _("_Yes"),
2144 NULL, NULL, ALERTFOCUS_SECOND);
2145 break;
2146 case NEXTUNREADMSGDIALOG_ASSUME_YES:
2147 val = G_ALERTALTERNATE;
2148 break;
2149 case NEXTUNREADMSGDIALOG_ASSUME_NO:
2150 val = G_ALERTOTHER;
2151 break;
2152 default:
2153 debug_print(
2154 _("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"));
2156 if (val == G_ALERTALTERNATE)
2157 folderview_select_next_with_flag(summaryview->folderview, MSG_MARKED);
2161 void summary_select_prev_labeled(SummaryView *summaryview)
2163 GtkCMCTreeNode *node;
2165 if (summaryview->sort_type == SORT_ASCENDING)
2166 node = summary_find_prev_flagged_msg
2167 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2168 else
2169 node = summary_find_next_flagged_msg
2170 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2172 if (!node) {
2173 AlertValue val;
2175 val = alertpanel(_("No more labeled messages"),
2176 _("No labeled message found. "
2177 "Search from the end?"),
2178 NULL, _("_No"), NULL, _("_Yes"),
2179 NULL, NULL, ALERTFOCUS_SECOND);
2180 if (val != G_ALERTALTERNATE) return;
2181 node = summary_find_prev_flagged_msg(summaryview, NULL,
2182 MSG_CLABEL_FLAG_MASK, TRUE);
2185 if (!node)
2186 alertpanel_notice(_("No labeled messages."));
2187 else
2188 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2191 void summary_select_next_labeled(SummaryView *summaryview)
2193 GtkCMCTreeNode *node;
2195 if (summaryview->sort_type == SORT_ASCENDING)
2196 node = summary_find_next_flagged_msg
2197 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2198 else
2199 node = summary_find_prev_flagged_msg
2200 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE);
2202 if (!node) {
2203 AlertValue val;
2205 val = alertpanel(_("No more labeled messages"),
2206 _("No labeled message found. "
2207 "Search from the beginning?"),
2208 NULL, _("_No"), NULL, _("_Yes"),
2209 NULL, NULL, ALERTFOCUS_SECOND);
2210 if (val != G_ALERTALTERNATE) return;
2211 if (summaryview->sort_type == SORT_ASCENDING)
2212 node = summary_find_next_flagged_msg(summaryview, NULL,
2213 MSG_CLABEL_FLAG_MASK, TRUE);
2214 else
2215 node = summary_find_prev_flagged_msg(summaryview, NULL,
2216 MSG_CLABEL_FLAG_MASK, TRUE);
2219 if (!node)
2220 alertpanel_notice(_("No labeled messages."));
2221 else
2222 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2225 void summary_select_parent(SummaryView *summaryview)
2227 GtkCMCTreeNode *node = NULL;
2229 if (summaryview->selected)
2230 node = GTK_CMCTREE_ROW(summaryview->selected)->parent;
2231 if (node)
2232 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT);
2235 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum,
2236 gboolean show)
2238 GtkCMCTreeNode *node;
2240 node = summary_find_msg_by_msgnum(summaryview, msgnum);
2241 summary_select_node(summaryview, node, show);
2244 void summary_select_by_msg_list(SummaryView *summaryview, GSList *msginfos)
2246 GtkCMCTree *ctree;
2247 GSList *msgnum_list, *walk;
2248 gboolean froze = FALSE;
2250 ctree = GTK_CMCTREE(summaryview->ctree);
2252 msgnum_list = procmsg_get_number_list_for_msgs(msginfos);
2254 START_LONG_OPERATION(summaryview, FALSE);
2255 for(walk = msgnum_list; walk; walk = walk->next) {
2256 GtkCMCTreeNode *node;
2257 node = summary_find_msg_by_msgnum(summaryview, GPOINTER_TO_UINT(walk->data));
2258 if (node != NULL)
2259 gtk_cmctree_select(ctree, node);
2261 END_LONG_OPERATION(summaryview);
2262 g_slist_free(msgnum_list);
2265 typedef struct _PostponedSelectData
2267 GtkCMCTree *ctree;
2268 GtkCMCTreeNode *row;
2269 GtkCMCTreeNode *node;
2270 GtkScrollType type;
2271 gint column;
2272 SummaryView *summaryview;
2273 gboolean display_msg;
2274 } PostponedSelectData;
2276 static gboolean summary_select_retry(void *data)
2278 PostponedSelectData *psdata = (PostponedSelectData *)data;
2279 debug_print("trying again\n");
2280 if (psdata->row)
2281 summary_selected(psdata->ctree, psdata->row,
2282 psdata->column, psdata->summaryview);
2283 else if (psdata->node)
2284 summary_select_node(psdata->summaryview, psdata->node,
2285 psdata->display_msg);
2286 g_free(psdata);
2287 return FALSE;
2291 * summary_select_node:
2292 * @summaryview: Summary view.
2293 * @node: Summary tree node.
2294 * @display_msg: whether to also display the message
2296 * Select @node (bringing it into view by scrolling and expanding its
2297 * thread, if necessary) and unselect all others.
2300 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node,
2301 gboolean display_msg)
2303 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2305 /* If msgview is hidden, we never want to automatically display
2306 * a selected message, since that would unhide the msgview. */
2307 if (!messageview_is_visible(summaryview->messageview))
2308 display_msg = FALSE;
2310 if (summary_is_locked(summaryview)
2311 && !GTK_SCTREE(ctree)->selecting_range
2312 && summaryview->messageview->mimeview
2313 && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
2314 && summaryview->messageview->mimeview->textview->loading) {
2315 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2316 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
2318 data->ctree = ctree;
2319 data->row = NULL;
2320 data->node = node;
2321 data->summaryview = summaryview;
2322 data->display_msg = display_msg;
2323 debug_print("postponing open of message till end of load\n");
2324 g_timeout_add(100, summary_select_retry, data);
2325 return;
2327 if (summary_is_locked(summaryview)) {
2328 return;
2330 if (!summaryview->folder_item)
2331 return;
2332 if (node) {
2333 gtkut_ctree_expand_parent_all(ctree, node);
2335 summary_lock(summaryview);
2336 GTK_EVENTS_FLUSH();
2337 summary_unlock(summaryview);
2339 /* If quicksearch has focus, let's keep it there. */
2340 if (!quicksearch_has_focus(summaryview->quicksearch) ||
2341 quicksearch_is_running(summaryview->quicksearch))
2342 summary_grab_focus(summaryview);
2344 gtkut_ctree_node_move_if_on_the_edge(ctree, node, -1);
2346 if (display_msg && summaryview->displayed == node)
2347 summaryview->displayed = NULL;
2348 summaryview->display_msg = display_msg;
2349 gtk_sctree_select(GTK_SCTREE(ctree), node);
2350 if (summaryview->selected == NULL)
2351 summaryview->selected = node;
2355 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node)
2357 GtkCMCTree *ctree =NULL;
2358 MsgInfo *msginfo;
2360 if (!summaryview)
2361 return 0;
2362 ctree = GTK_CMCTREE(summaryview->ctree);
2363 if (!node)
2364 return 0;
2365 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2366 if (msginfo)
2367 return msginfo->msgnum;
2368 else
2369 return -1;
2372 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview,
2373 GtkCMCTreeNode *current_node,
2374 gboolean start_from_prev)
2376 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2377 GtkCMCTreeNode *node;
2378 MsgInfo *msginfo;
2380 if (current_node) {
2381 if (start_from_prev)
2382 node = gtkut_ctree_node_prev(ctree, current_node);
2383 else
2384 node = current_node;
2385 } else
2386 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2388 for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2389 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2390 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break;
2393 return node;
2396 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview,
2397 GtkCMCTreeNode *current_node,
2398 gboolean start_from_next)
2400 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2401 GtkCMCTreeNode *node;
2402 MsgInfo *msginfo;
2404 if (current_node) {
2405 if (start_from_next)
2406 node = gtkut_ctree_node_next(ctree, current_node);
2407 else
2408 node = current_node;
2409 } else
2410 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2412 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2413 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2414 if (msginfo && !MSG_IS_DELETED(msginfo->flags)
2415 && !MSG_IS_MOVE(msginfo->flags)) break;
2418 return node;
2421 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview,
2422 GtkCMCTreeNode *current_node,
2423 MsgPermFlags flags,
2424 gboolean start_from_prev)
2426 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2427 GtkCMCTreeNode *node;
2428 MsgInfo *msginfo;
2430 if (current_node) {
2431 if (start_from_prev)
2432 node = gtkut_ctree_node_prev(ctree, current_node);
2433 else
2434 node = current_node;
2435 } else
2436 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1);
2438 for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) {
2439 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2440 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break;
2443 return node;
2446 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview,
2447 GtkCMCTreeNode *current_node,
2448 MsgPermFlags flags,
2449 gboolean start_from_next)
2451 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2452 GtkCMCTreeNode *node;
2453 MsgInfo *msginfo;
2455 if (current_node) {
2456 if (start_from_next)
2457 node = gtkut_ctree_node_next(ctree, current_node);
2458 else
2459 node = current_node;
2460 } else
2461 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2463 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2464 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2465 /* Find msg with matching flags but ignore messages with
2466 ignore flags, if searching for new or unread messages */
2467 if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) &&
2468 !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags))
2470 break;
2473 return node;
2476 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview,
2477 guint msgnum)
2479 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2480 GtkCMCTreeNode *node;
2481 MsgInfo *msginfo;
2483 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2485 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2486 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2487 if (msginfo && msginfo->msgnum == msgnum) break;
2490 return node;
2493 static guint attract_hash_func(gconstpointer key)
2495 gchar *str;
2496 gchar *p;
2497 guint h;
2499 Xstrdup_a(str, (const gchar *)key, return 0);
2500 trim_subject(str);
2502 p = str;
2503 h = *p;
2505 if (h) {
2506 for (p += 1; *p != '\0'; p++)
2507 h = (h << 5) - h + *p;
2510 return h;
2513 static gint attract_compare_func(gconstpointer a, gconstpointer b)
2515 return subject_compare((const gchar *)a, (const gchar *)b) == 0;
2518 void summary_attract_by_subject(SummaryView *summaryview)
2520 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2521 GtkCMCList *clist = GTK_CMCLIST(ctree);
2522 GtkCMCTreeNode *src_node;
2523 GtkCMCTreeNode *dst_node, *sibling;
2524 GtkCMCTreeNode *tmp;
2525 MsgInfo *src_msginfo, *dst_msginfo;
2526 GHashTable *subject_table;
2528 debug_print("Attracting messages by subject...\n");
2529 STATUSBAR_PUSH(summaryview->mainwin,
2530 _("Attracting messages by subject..."));
2532 main_window_cursor_wait(summaryview->mainwin);
2533 summary_freeze(summaryview);
2535 subject_table = g_hash_table_new(attract_hash_func,
2536 attract_compare_func);
2538 for (src_node = GTK_CMCTREE_NODE(clist->row_list);
2539 src_node != NULL;
2540 src_node = tmp) {
2541 tmp = GTK_CMCTREE_ROW(src_node)->sibling;
2542 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node);
2543 if (!src_msginfo) continue;
2544 if (!src_msginfo->subject) continue;
2546 /* find attracting node */
2547 dst_node = g_hash_table_lookup(subject_table,
2548 src_msginfo->subject);
2550 if (dst_node) {
2551 dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node);
2553 /* if the time difference is more than 20 days,
2554 don't attract */
2555 if (ABS(src_msginfo->date_t - dst_msginfo->date_t)
2556 > 60 * 60 * 24 * 20)
2557 continue;
2559 sibling = GTK_CMCTREE_ROW(dst_node)->sibling;
2560 if (src_node != sibling)
2561 gtk_cmctree_move(ctree, src_node, NULL, sibling);
2564 g_hash_table_insert(subject_table,
2565 src_msginfo->subject, src_node);
2568 g_hash_table_destroy(subject_table);
2570 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
2572 summary_thaw(summaryview);
2574 debug_print("Attracting messages by subject done.\n");
2575 STATUSBAR_POP(summaryview->mainwin);
2577 main_window_cursor_normal(summaryview->mainwin);
2580 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2581 gpointer data)
2583 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2585 if (msginfo)
2586 procmsg_msginfo_free(&msginfo);
2589 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2590 gpointer data)
2592 SummaryView *summaryview = data;
2593 MsgInfo *msginfo;
2595 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
2597 cm_return_if_fail(msginfo != NULL);
2599 if (MSG_IS_DELETED(msginfo->flags))
2600 summaryview->deleted++;
2602 summaryview->total_size += msginfo->size;
2604 summary_set_row_marks(summaryview, node);
2607 static void summary_update_status(SummaryView *summaryview)
2609 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2610 GtkCMCTreeNode *node;
2611 MsgInfo *msginfo;
2613 summaryview->total_size =
2614 summaryview->deleted = summaryview->moved = summaryview->copied = 0;
2616 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
2617 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
2618 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
2620 if (!msginfo)
2621 continue;
2623 if (MSG_IS_DELETED(msginfo->flags))
2624 summaryview->deleted++;
2625 if (MSG_IS_MOVE(msginfo->flags))
2626 summaryview->moved++;
2627 if (MSG_IS_COPY(msginfo->flags))
2628 summaryview->copied++;
2629 summaryview->total_size += msginfo->size;
2633 static void summary_status_show(SummaryView *summaryview)
2635 gchar *str;
2636 gchar *del, *mv, *cp;
2637 gchar *sel;
2638 gchar *spc;
2639 gchar *itstr;
2640 GList *rowlist, *cur;
2641 guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0;
2642 guint n_marked = 0, n_replied = 0, n_forwarded = 0, n_locked = 0, n_ignored = 0, n_watched = 0;
2643 goffset sel_size = 0, n_size = 0;
2644 MsgInfo *msginfo;
2645 gchar *name;
2646 gchar *tooltip;
2648 if (!summaryview->folder_item) {
2649 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), "");
2650 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), "");
2651 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), "");
2652 toolbar_main_set_sensitive(summaryview->mainwin);
2653 return;
2655 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_folder), PANGO_ELLIPSIZE_END);
2656 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_select), PANGO_ELLIPSIZE_END);
2657 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_msgs), PANGO_ELLIPSIZE_START);
2659 rowlist = GTK_CMCLIST(summaryview->ctree)->selection;
2660 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2661 msginfo = gtk_cmctree_node_get_row_data
2662 (GTK_CMCTREE(summaryview->ctree),
2663 GTK_CMCTREE_NODE(cur->data));
2664 if (msginfo) {
2665 sel_size += msginfo->size;
2666 n_selected++;
2670 if (summaryview->folder_item->hide_read_msgs
2671 || summaryview->folder_item->hide_del_msgs
2672 || summaryview->folder_item->hide_read_threads
2673 || quicksearch_has_sat_predicate(summaryview->quicksearch)) {
2674 rowlist = GTK_CMCLIST(summaryview->ctree)->row_list;
2675 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) {
2676 msginfo = gtk_cmctree_node_get_row_data
2677 (GTK_CMCTREE(summaryview->ctree),
2678 GTK_CMCTREE_NODE(cur));
2679 if (msginfo) {
2680 n_size += msginfo->size;
2681 n_total++;
2682 if (MSG_IS_NEW(msginfo->flags))
2683 n_new++;
2684 if (MSG_IS_UNREAD(msginfo->flags))
2685 n_unread++;
2686 if (MSG_IS_MARKED(msginfo->flags))
2687 n_marked++;
2688 if (MSG_IS_REPLIED(msginfo->flags))
2689 n_replied++;
2690 if (MSG_IS_FORWARDED(msginfo->flags))
2691 n_forwarded++;
2692 if (MSG_IS_LOCKED(msginfo->flags))
2693 n_locked++;
2694 if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2695 n_ignored++;
2696 if (MSG_IS_WATCH_THREAD(msginfo->flags))
2697 n_watched++;
2700 } else {
2701 n_new = summaryview->folder_item->new_msgs;
2702 n_unread = summaryview->folder_item->unread_msgs;
2703 n_marked = summaryview->folder_item->marked_msgs;
2704 n_replied = summaryview->folder_item->replied_msgs;
2705 n_forwarded = summaryview->folder_item->forwarded_msgs;
2706 n_locked = summaryview->folder_item->locked_msgs;
2707 n_ignored = summaryview->folder_item->ignored_msgs;
2708 n_watched = summaryview->folder_item->watched_msgs;
2709 n_total = summaryview->folder_item->total_msgs;
2710 n_size = summaryview->total_size;
2713 name = folder_item_get_name(summaryview->folder_item);
2714 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
2715 g_free(name);
2717 if (summaryview->deleted)
2718 del = g_strdup_printf(_("%d deleted"), summaryview->deleted);
2719 else
2720 del = g_strdup("");
2721 if (summaryview->moved)
2722 mv = g_strdup_printf(_("%s%d moved"),
2723 summaryview->deleted ? _(", ") : "",
2724 summaryview->moved);
2725 else
2726 mv = g_strdup("");
2727 if (summaryview->copied)
2728 cp = g_strdup_printf(_("%s%d copied"),
2729 summaryview->deleted ||
2730 summaryview->moved ? _(", ") : "",
2731 summaryview->copied);
2732 else
2733 cp = g_strdup("");
2735 if (summaryview->deleted || summaryview->moved || summaryview->copied)
2736 spc = " ";
2737 else
2738 spc = "";
2740 if (n_selected) {
2741 sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size));
2742 itstr = g_strdup_printf(ngettext(" item selected"," items selected", n_selected));
2743 } else {
2744 sel = g_strdup("");
2745 itstr = g_strdup("");
2748 if (prefs_common.layout_mode != SMALL_LAYOUT) {
2749 str = g_strconcat(n_selected ? itos(n_selected) : "",
2750 itstr, sel, spc, del, mv, cp, NULL);
2751 g_free(sel);
2752 g_free(del);
2753 g_free(mv);
2754 g_free(cp);
2755 g_free(itstr);
2757 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2758 g_free(str);
2760 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2761 n_new, n_unread, n_total,
2762 to_human_readable((goffset)n_size));
2764 g_signal_connect(G_OBJECT(summaryview->popupmenu), "selection-done",
2765 G_CALLBACK(popup_menu_selection_done), summaryview);
2767 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str);
2768 g_free(str);
2769 tooltip = g_strdup_printf("<b>%s</b>\n"
2770 "<b>%s</b> %d\n"
2771 "<b>%s</b> %d\n"
2772 "<b>%s</b> %d\n"
2773 "<b>%s</b> %s\n\n"
2774 "<b>%s</b> %d\n"
2775 "<b>%s</b> %d\n"
2776 "<b>%s</b> %d\n"
2777 "<b>%s</b> %d\n"
2778 "<b>%s</b> %d\n"
2779 "<b>%s</b> %d",
2780 _("Message summary"),
2781 _("New:"), n_new,
2782 _("Unread:"), n_unread,
2783 _("Total:"), n_total,
2784 _("Size:"), to_human_readable((goffset)n_size),
2785 _("Marked:"), n_marked,
2786 _("Replied:"), n_replied,
2787 _("Forwarded:"), n_forwarded,
2788 _("Locked:"), n_locked,
2789 _("Ignored:"), n_ignored,
2790 _("Watched:"), n_watched);
2792 gtk_widget_set_tooltip_markup(GTK_WIDGET(summaryview->statlabel_msgs),
2793 tooltip);
2794 g_free(tooltip);
2795 } else {
2796 gchar *ssize, *tsize;
2797 if (n_selected) {
2798 ssize = g_strdup(to_human_readable((goffset)sel_size));
2799 tsize = g_strdup(to_human_readable((goffset)n_size));
2800 str = g_strdup_printf(_("%d/%d selected (%s/%s), %d unread"),
2801 n_selected, n_total, ssize, tsize, n_unread);
2802 g_free(ssize);
2803 g_free(tsize);
2804 } else
2805 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"),
2806 n_new, n_unread, n_total, to_human_readable((goffset)n_size));
2807 g_free(sel);
2808 g_free(del);
2809 g_free(mv);
2810 g_free(cp);
2811 g_free(itstr);
2813 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str);
2814 g_free(str);
2817 summary_set_menu_sensitive(summaryview);
2818 toolbar_main_set_sensitive(summaryview->mainwin);
2821 static void summary_set_column_titles(SummaryView *summaryview)
2823 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
2824 FolderItem *item = summaryview->folder_item;
2825 GtkWidget *hbox;
2826 GtkWidget *label;
2827 GtkWidget *arrow;
2828 gint pos;
2829 const gchar *title;
2830 SummaryColumnType type;
2831 GtkJustification justify;
2833 static FolderSortKey sort_by[N_SUMMARY_COLS] = {
2834 SORT_BY_MARK,
2835 SORT_BY_STATUS,
2836 SORT_BY_MIME,
2837 SORT_BY_SUBJECT,
2838 SORT_BY_FROM,
2839 SORT_BY_TO,
2840 SORT_BY_DATE,
2841 SORT_BY_SIZE,
2842 SORT_BY_NUMBER,
2843 SORT_BY_SCORE,
2844 SORT_BY_LOCKED,
2845 SORT_BY_TAGS
2848 for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
2849 type = summaryview->col_state[pos].type;
2851 /* CLAWS: mime and unread are single char headers */
2852 justify = (type == S_COL_NUMBER || type == S_COL_SIZE)
2853 ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT;
2855 switch (type) {
2856 case S_COL_SUBJECT:
2857 case S_COL_FROM:
2858 case S_COL_TO:
2859 case S_COL_DATE:
2860 case S_COL_NUMBER:
2861 if(type == S_COL_FROM && item != NULL &&
2862 FOLDER_SHOWS_TO_HDR(item) &&
2863 !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
2864 type = S_COL_TO;
2865 if(type == S_COL_NUMBER)
2866 title = gettext(col_label[type]);
2867 else
2868 title = prefs_common_translated_header_name(col_label[type]);
2869 break;
2870 default:
2871 title = gettext(col_label[type]);
2874 if (type == S_COL_MIME) {
2875 label = gtk_image_new_from_pixbuf(clipxpm);
2876 gtk_widget_show(label);
2877 gtk_cmclist_set_column_widget(clist, pos, label);
2878 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment"));
2879 continue;
2880 } else if (type == S_COL_MARK) {
2881 label = gtk_image_new_from_pixbuf(markxpm);
2882 gtk_widget_show(label);
2883 gtk_cmclist_set_column_widget(clist, pos, label);
2884 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Mark"));
2885 continue;
2886 } else if (type == S_COL_LOCKED) {
2887 label = gtk_image_new_from_pixbuf(lockedxpm);
2888 gtk_widget_show(label);
2889 gtk_cmclist_set_column_widget(clist, pos, label);
2890 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked"));
2891 continue;
2892 } else if (type == S_COL_STATUS) {
2893 gtk_cmclist_set_column_title(clist, pos, title);
2894 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status"));
2895 continue;
2898 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
2899 label = gtk_label_new(title);
2900 #ifdef GENERIC_UMPC
2901 gtk_widget_set_size_request(hbox, -1, 20);
2902 #endif
2904 if (justify == GTK_JUSTIFY_RIGHT)
2905 gtk_box_pack_end(GTK_BOX(hbox), label,
2906 FALSE, FALSE, 0);
2907 else
2908 gtk_box_pack_start(GTK_BOX(hbox), label,
2909 FALSE, FALSE, 0);
2911 if (summaryview->sort_key == sort_by[type] ||
2912 (summaryview->sort_key == SORT_BY_THREAD_DATE &&
2913 sort_by[SORT_BY_DATE] && type == S_COL_DATE)) {
2914 arrow = gtk_image_new_from_icon_name
2915 (summaryview->sort_type == SORT_ASCENDING
2916 ? "pan-down-symbolic" : "pan-up-symbolic",
2917 GTK_ICON_SIZE_MENU);
2918 gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10);
2919 if (justify == GTK_JUSTIFY_RIGHT)
2920 gtk_box_pack_start(GTK_BOX(hbox), arrow,
2921 FALSE, FALSE, 0);
2922 else
2923 gtk_box_pack_end(GTK_BOX(hbox), arrow,
2924 FALSE, FALSE, 0);
2927 gtk_widget_show_all(hbox);
2928 gtk_cmclist_set_column_widget(clist, pos, hbox);
2932 void summary_reflect_tags_changes(SummaryView *summaryview)
2934 GtkMenuShell *menu;
2935 GList *children, *cur;
2936 GtkCMCTreeNode *node;
2937 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
2938 gboolean froze = FALSE;
2939 gboolean redisplay = FALSE;
2941 /* re-create colorlabel submenu */
2942 menu = GTK_MENU_SHELL(summaryview->tags_menu);
2943 cm_return_if_fail(menu != NULL);
2945 /* clear items. get item pointers. */
2946 children = gtk_container_get_children(GTK_CONTAINER(menu));
2947 for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
2948 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
2950 g_list_free(children);
2951 summary_tags_menu_create(summaryview, TRUE);
2953 START_LONG_OPERATION(summaryview, TRUE);
2954 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
2955 node = gtkut_ctree_node_next(ctree, node)) {
2956 redisplay |= summary_set_row_tag(summaryview,
2957 node, TRUE, FALSE, 0);
2959 END_LONG_OPERATION(summaryview);
2960 if (redisplay)
2961 summary_redisplay_msg(summaryview);
2965 void summary_reflect_prefs(void)
2967 static gchar *last_smallfont = NULL;
2968 static gchar *last_normalfont = NULL;
2969 static gchar *last_boldfont = NULL;
2970 static gboolean last_derive = 0;
2971 gboolean update_font = FALSE;
2972 SummaryView *summaryview = NULL;
2974 if (!mainwindow_get_mainwindow())
2975 return;
2976 summaryview = mainwindow_get_mainwindow()->summaryview;
2978 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2979 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2980 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2981 last_derive != prefs_common.derive_from_normal_font)
2982 update_font = TRUE;
2984 g_free(last_smallfont);
2985 last_smallfont = g_strdup(SMALL_FONT);
2986 g_free(last_normalfont);
2987 last_normalfont = g_strdup(NORMAL_FONT);
2988 g_free(last_boldfont);
2989 last_boldfont = g_strdup(BOLD_FONT);
2990 last_derive = prefs_common.derive_from_normal_font;
2992 #define STYLE_FREE(s) \
2993 if (s != NULL) { \
2994 g_object_unref(s); \
2995 s = NULL; \
2998 if (update_font) {
2999 STYLE_FREE(bold_style);
3000 STYLE_FREE(bold_style);
3001 summary_set_fonts(summaryview);
3004 #undef STYLE_FREE
3006 summary_set_column_titles(summaryview);
3007 summary_relayout(summaryview);
3009 if (summaryview->folder_item)
3010 summary_show(summaryview, summaryview->folder_item, FALSE);
3013 void summary_sort(SummaryView *summaryview,
3014 FolderSortKey sort_key, FolderSortType sort_type)
3016 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3017 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
3018 GtkCMCListCompareFunc cmp_func = NULL;
3019 START_TIMING("");
3020 g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree),
3021 G_CALLBACK(summary_tree_expanded), summaryview);
3022 summary_freeze(summaryview);
3024 switch (sort_key) {
3025 case SORT_BY_MARK:
3026 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark;
3027 break;
3028 case SORT_BY_STATUS:
3029 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status;
3030 break;
3031 case SORT_BY_MIME:
3032 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime;
3033 break;
3034 case SORT_BY_NUMBER:
3035 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num;
3036 break;
3037 case SORT_BY_SIZE:
3038 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size;
3039 break;
3040 case SORT_BY_DATE:
3041 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date;
3042 break;
3043 case SORT_BY_THREAD_DATE:
3044 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date;
3045 break;
3046 case SORT_BY_FROM:
3047 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from;
3048 break;
3049 case SORT_BY_SUBJECT:
3050 if (summaryview->simplify_subject_preg)
3051 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject;
3052 else
3053 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject;
3054 break;
3055 case SORT_BY_SCORE:
3056 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score;
3057 break;
3058 case SORT_BY_LABEL:
3059 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label;
3060 break;
3061 case SORT_BY_TO:
3062 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to;
3063 break;
3064 case SORT_BY_LOCKED:
3065 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked;
3066 break;
3067 case SORT_BY_TAGS:
3068 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_tags;
3069 break;
3070 case SORT_BY_NONE:
3071 break;
3072 default:
3073 goto unlock;
3076 summaryview->sort_key = sort_key;
3077 summaryview->sort_type = sort_type;
3079 summary_set_column_titles(summaryview);
3080 summary_set_menu_sensitive(summaryview);
3082 /* allow fallback to don't sort */
3083 if (summaryview->sort_key == SORT_BY_NONE)
3084 goto unlock;
3086 if (cmp_func != NULL) {
3087 debug_print("Sorting summary...\n");
3088 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary..."));
3090 main_window_cursor_wait(summaryview->mainwin);
3092 gtk_cmclist_set_compare_func(clist, cmp_func);
3094 gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type);
3095 gtk_sctree_sort_recursive(ctree, NULL);
3097 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
3099 main_window_cursor_normal(summaryview->mainwin);
3101 debug_print("Sorting summary done.\n");
3102 STATUSBAR_POP(summaryview->mainwin);
3104 unlock:
3105 summary_thaw(summaryview);
3106 g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree),
3107 G_CALLBACK(summary_tree_expanded), summaryview);
3108 END_TIMING();
3111 static gboolean summary_update_thread_age(GNode *node, gpointer data)
3113 MsgInfo *msginfo = node->data;
3114 time_t *most_recent = (time_t *)data;
3116 if (msginfo->date_t > *most_recent) {
3117 *most_recent = msginfo->date_t;
3119 return FALSE;
3122 static void summary_find_thread_age(GNode *gnode)
3124 MsgInfo *msginfo = (MsgInfo *)gnode->data;
3125 time_t most_recent;
3127 if (!msginfo)
3128 return;
3129 most_recent = msginfo->thread_date = msginfo->date_t;
3131 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent);
3133 msginfo->thread_date = most_recent;
3136 static gboolean summary_update_is_read(GNode *node, gpointer data)
3138 MsgInfo *msginfo = node->data;
3139 gboolean *all_read = (gboolean *)data;
3141 if (MSG_IS_UNREAD(msginfo->flags)) {
3142 *all_read = FALSE;
3143 return TRUE;
3145 return FALSE;
3148 static gboolean summary_thread_is_read(GNode *gnode)
3150 MsgInfo *msginfo = (MsgInfo *)gnode->data;
3151 gboolean all_read = TRUE;
3153 if (!msginfo)
3154 return all_read;
3156 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_is_read, &all_read);
3157 return all_read;
3160 typedef struct _ThreadSelectedData {
3161 guint msgnum;
3162 gboolean is_selected;
3163 } ThreadSelectedData;
3165 static gboolean summary_update_is_selected(GNode *gnode, gpointer data)
3167 ThreadSelectedData *selected = (ThreadSelectedData *)data;
3168 MsgInfo *msginfo = (MsgInfo *)gnode->data;
3170 if (msginfo->msgnum == selected->msgnum) {
3171 selected->is_selected = TRUE;
3172 return TRUE;
3175 return FALSE;
3178 static gboolean summary_thread_is_selected(GNode *gnode, guint selected_msgnum)
3180 ThreadSelectedData selected;
3182 selected.msgnum = selected_msgnum;
3183 selected.is_selected = FALSE;
3184 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1,
3185 summary_update_is_selected, &selected);
3186 return selected.is_selected;
3189 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode,
3190 GtkCMCTreeNode *cnode, gpointer data)
3192 SummaryView *summaryview = (SummaryView *)data;
3193 MsgInfo *msginfo = (MsgInfo *)gnode->data;
3194 gchar *text[N_SUMMARY_COLS];
3195 gint *col_pos = summaryview->col_pos;
3196 const gchar *msgid = msginfo->msgid;
3197 GHashTable *msgid_table = summaryview->msgid_table;
3198 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3199 gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3201 summary_set_header(summaryview, text, msginfo);
3203 gtk_cmctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2,
3204 NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed);
3205 #define SET_TEXT(col) { \
3206 gtk_cmctree_node_set_text(ctree, cnode, col_pos[col], \
3207 text[col_pos[col]]); \
3210 if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3211 SET_TEXT(S_COL_NUMBER);
3212 if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3213 SET_TEXT(S_COL_SCORE);
3214 if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3215 SET_TEXT(S_COL_SIZE);
3216 if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible)
3217 SET_TEXT(S_COL_DATE);
3218 if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
3219 SET_TEXT(S_COL_FROM);
3220 if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
3221 SET_TEXT(S_COL_TO);
3222 if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible)
3223 SET_TEXT(S_COL_TAGS);
3225 if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3226 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3228 #undef SET_TEXT
3230 GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo);
3231 summary_set_marks_func(ctree, cnode, summaryview);
3233 if (msgid && msgid[0] != '\0')
3234 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode);
3236 return TRUE;
3239 static void summary_set_ctree_from_list(SummaryView *summaryview,
3240 GSList *mlist, guint selected_msgnum)
3242 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3243 MsgInfo *msginfo;
3244 GtkCMCTreeNode *node = NULL;
3245 GHashTable *msgid_table;
3246 GHashTable *subject_table = NULL;
3247 GSList * cur;
3248 GdkDisplay *display;
3250 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3251 gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3252 START_TIMING("");
3254 if (!mlist) return;
3256 display = gdk_display_get_default();
3258 debug_print("Setting summary from message data...\n");
3259 STATUSBAR_PUSH(summaryview->mainwin,
3260 _("Setting summary from message data..."));
3261 gdk_display_flush(display);
3263 g_signal_handlers_block_by_func(G_OBJECT(ctree),
3264 G_CALLBACK(summary_tree_expanded), summaryview);
3266 msgid_table = g_hash_table_new(g_str_hash, g_str_equal);
3267 summaryview->msgid_table = msgid_table;
3269 if (prefs_common.thread_by_subject) {
3270 subject_table = g_hash_table_new(g_str_hash, g_str_equal);
3271 summaryview->subject_table = subject_table;
3272 } else {
3273 summaryview->subject_table = NULL;
3276 if (prefs_common.use_addr_book)
3277 start_address_completion(NULL);
3279 if (summaryview->threaded) {
3280 GNode *root, *gnode;
3281 START_TIMING("threaded");
3282 root = procmsg_get_thread_tree(mlist);
3285 for (gnode = root->children; gnode != NULL;
3286 gnode = gnode->next) {
3287 if (!summaryview->folder_item->hide_read_threads ||
3288 !summary_thread_is_read(gnode) ||
3289 summary_thread_is_selected(gnode, selected_msgnum)) {
3290 summary_find_thread_age(gnode);
3291 node = gtk_sctree_insert_gnode
3292 (ctree, NULL, node, gnode,
3293 summary_insert_gnode_func, summaryview);
3297 g_node_destroy(root);
3299 END_TIMING();
3300 } else {
3301 gchar *text[N_SUMMARY_COLS];
3302 START_TIMING("unthreaded");
3303 cur = mlist;
3304 for (; mlist != NULL; mlist = mlist->next) {
3305 msginfo = (MsgInfo *)mlist->data;
3307 summary_set_header(summaryview, text, msginfo);
3309 node = gtk_sctree_insert_node
3310 (ctree, NULL, node, text, 2,
3311 NULL, NULL,
3312 FALSE, FALSE);
3313 if ((vert_layout || small_layout) && prefs_common.two_line_vert)
3314 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]);
3316 GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo);
3317 summary_set_marks_func(ctree, node, summaryview);
3319 if (msginfo->msgid && msginfo->msgid[0] != '\0')
3320 g_hash_table_insert(msgid_table,
3321 msginfo->msgid, node);
3323 if (prefs_common.thread_by_subject)
3324 subject_table_insert(subject_table,
3325 msginfo->subject,
3326 node);
3328 mlist = cur;
3329 END_TIMING();
3332 if (prefs_common.enable_hscrollbar &&
3333 summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) {
3334 gint optimal_width;
3336 optimal_width = gtk_cmclist_optimal_column_width
3337 (GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]);
3338 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree),
3339 summaryview->col_pos[S_COL_SUBJECT],
3340 optimal_width);
3343 if (prefs_common.use_addr_book)
3344 end_address_completion();
3346 debug_print("Setting summary from message data done.\n");
3347 STATUSBAR_POP(summaryview->mainwin);
3348 if (debug_get_mode()) {
3349 debug_print("\tmsgid hash table size = %d\n",
3350 g_hash_table_size(msgid_table));
3351 if (prefs_common.thread_by_subject)
3352 debug_print("\tsubject hash table size = %d\n",
3353 g_hash_table_size(subject_table));
3356 summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type);
3358 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3360 if (prefs_common.bold_unread) {
3361 START_TIMING("bold_unread");
3362 while (node) {
3363 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
3364 if (GTK_CMCTREE_ROW(node)->children)
3365 summary_set_row_marks(summaryview, node);
3366 node = next;
3368 END_TIMING();
3371 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
3372 G_CALLBACK(summary_tree_expanded), summaryview);
3373 END_TIMING();
3376 static gchar *summary_complete_address(const gchar *addr)
3378 gint count;
3379 gchar *res, *tmp, *email_addr;
3381 if (addr == NULL || !strchr(addr, '@'))
3382 return NULL;
3384 Xstrdup_a(email_addr, addr, return NULL);
3385 extract_address(email_addr);
3386 if (!*email_addr)
3387 return NULL;
3390 * completion stuff must be already initialized
3392 res = NULL;
3393 if (1 < (count = complete_address(email_addr))) {
3394 tmp = get_complete_address(1);
3395 res = procheader_get_fromname(tmp);
3396 g_free(tmp);
3399 return res;
3402 static inline void summary_set_header(SummaryView *summaryview, gchar *text[],
3403 MsgInfo *msginfo)
3405 static gchar date_modified[80];
3406 static gchar col_score[11];
3407 static gchar from_buf[BUFFSIZE], to_buf[BUFFSIZE];
3408 static gchar tmp1[BUFFSIZE], tmp2[BUFFSIZE+4], tmp3[BUFFSIZE];
3409 gint *col_pos = summaryview->col_pos;
3410 gchar *from_text = NULL, *to_text = NULL, *tags_text = NULL;
3411 gboolean should_swap = FALSE;
3412 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
3413 gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
3414 static const gchar *color_dim_rgb = NULL;
3415 if (!color_dim_rgb)
3416 color_dim_rgb = gtkut_gdk_rgba_to_string(&summaryview->color_dim);
3417 text[col_pos[S_COL_FROM]] = "";
3418 text[col_pos[S_COL_TO]] = "";
3419 text[col_pos[S_COL_SUBJECT]]= "";
3420 text[col_pos[S_COL_MARK]] = "";
3421 text[col_pos[S_COL_STATUS]] = "";
3422 text[col_pos[S_COL_MIME]] = "";
3423 text[col_pos[S_COL_LOCKED]] = "";
3424 text[col_pos[S_COL_DATE]] = "";
3425 text[col_pos[S_COL_TAGS]] = "";
3426 if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible)
3427 text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum);
3428 else
3429 text[col_pos[S_COL_NUMBER]] = "";
3431 /* slow! */
3432 if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible)
3433 text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size);
3434 else
3435 text[col_pos[S_COL_SIZE]] = "";
3437 if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible)
3438 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score);
3439 else
3440 text[col_pos[S_COL_SCORE]] = "";
3442 if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
3443 tags_text = procmsg_msginfo_get_tags_str(msginfo);
3444 if (!tags_text) {
3445 text[col_pos[S_COL_TAGS]] = "-";
3446 } else {
3447 strncpy2(tmp1, tags_text, sizeof(tmp1));
3448 tmp1[sizeof(tmp1)-1]='\0';
3449 g_free(tags_text);
3450 text[col_pos[S_COL_TAGS]] = tmp1;
3452 } else
3453 text[col_pos[S_COL_TAGS]] = "";
3455 /* slow! */
3456 if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible ||
3457 ((vert_layout || small_layout) && prefs_common.two_line_vert)) {
3458 if (msginfo->date_t && msginfo->date_t > 0) {
3459 procheader_date_get_localtime(date_modified,
3460 sizeof(date_modified),
3461 msginfo->date_t);
3462 text[col_pos[S_COL_DATE]] = date_modified;
3463 } else if (msginfo->date)
3464 text[col_pos[S_COL_DATE]] = msginfo->date;
3465 else
3466 text[col_pos[S_COL_DATE]] = _("(No Date)");
3469 if (prefs_common.swap_from && msginfo->from && msginfo->to
3470 && !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) {
3471 gchar *addr = NULL;
3473 addr = g_strdup(msginfo->from);
3475 if (addr) {
3476 extract_address(addr);
3477 if (account_find_from_address(addr, FALSE)) {
3478 should_swap = TRUE;
3480 g_free(addr);
3484 if (!prefs_common.use_addr_book) {
3485 if (prefs_common.summary_from_show == SHOW_NAME)
3486 from_text = msginfo->fromname;
3487 else if (prefs_common.summary_from_show == SHOW_BOTH)
3488 from_text = msginfo->from;
3489 else {
3490 from_text = msginfo->from;
3491 extract_address(from_text);
3493 if (!from_text)
3494 from_text = _("(No From)");
3495 } else {
3496 gchar *tmp = summary_complete_address(msginfo->from);
3497 if (tmp) {
3498 strncpy2(from_buf, tmp, sizeof(from_buf));
3499 g_free(tmp);
3500 from_text = from_buf;
3501 } else {
3502 if (prefs_common.summary_from_show == SHOW_NAME)
3503 from_text = msginfo->fromname;
3504 else if (prefs_common.summary_from_show == SHOW_BOTH)
3505 from_text = msginfo->from;
3506 else {
3507 from_text = msginfo->from;
3508 if (from_text)
3509 extract_address(from_text);
3511 if (!from_text)
3512 from_text = _("(No From)");
3516 to_text = msginfo->to ? msginfo->to :
3517 (msginfo->cc ? msginfo->cc :
3518 (msginfo->newsgroups ? msginfo->newsgroups : NULL
3522 if (!to_text)
3523 to_text = _("(No Recipient)");
3524 else {
3525 if (prefs_common.summary_from_show == SHOW_NAME) {
3526 gchar *tmp = procheader_get_fromname(to_text);
3527 /* need to keep to_text pointing to stack, so heap-allocated
3528 * string from procheader_get_fromname() will be copied to to_buf */
3529 if (tmp != NULL) {
3530 strncpy2(to_buf, tmp, sizeof(to_buf));
3531 g_free(tmp);
3532 to_text = to_buf;
3534 } else if (prefs_common.summary_from_show == SHOW_ADDR)
3535 extract_address(to_text);
3538 text[col_pos[S_COL_TO]] = to_text;
3539 if (!should_swap) {
3540 text[col_pos[S_COL_FROM]] = from_text;
3541 } else {
3542 if (prefs_common.use_addr_book) {
3543 gchar *tmp = summary_complete_address(to_text);
3544 /* need to keep to_text pointing to stack, so heap-allocated
3545 * string from summary_complete_address() will be copied to to_buf */
3546 if (tmp) {
3547 strncpy2(to_buf, tmp, sizeof(to_buf));
3548 g_free(tmp);
3549 to_text = to_buf;
3550 } else {
3551 to_text = to_text ? to_text : _("(No From)");
3554 snprintf(tmp2, BUFFSIZE+4, "➜ %s", to_text);
3555 tmp2[BUFFSIZE-1]='\0';
3556 text[col_pos[S_COL_FROM]] = tmp2;
3559 if (summaryview->simplify_subject_preg != NULL)
3560 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ?
3561 string_remove_match(tmp3, BUFFSIZE, msginfo->subject,
3562 summaryview->simplify_subject_preg) :
3563 _("(No Subject)");
3564 else
3565 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject :
3566 _("(No Subject)");
3567 if ((vert_layout || small_layout) && prefs_common.two_line_vert) {
3568 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) {
3569 gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3570 "<span color='%s' style='italic'>",
3571 _("From: %s, on %s"), "</span>", NULL),
3572 text[col_pos[S_COL_SUBJECT]],
3573 color_dim_rgb,
3574 text[col_pos[S_COL_FROM]],
3575 text[col_pos[S_COL_DATE]]);
3576 text[col_pos[S_COL_SUBJECT]] = tmp;
3577 } else {
3578 gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n",
3579 "<span color='%s' style='italic'>",
3580 _("To: %s, on %s"), "</span>", NULL),
3581 text[col_pos[S_COL_SUBJECT]],
3582 color_dim_rgb,
3583 text[col_pos[S_COL_TO]],
3584 text[col_pos[S_COL_DATE]]);
3585 text[col_pos[S_COL_SUBJECT]] = tmp;
3590 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row)
3592 summary_display_msg_full(summaryview, row, FALSE, FALSE);
3595 static gboolean defer_change(gpointer data);
3596 typedef enum {
3597 FLAGS_UNSET,
3598 FLAGS_SET,
3599 FLAGS_CHANGE
3600 } ChangeType;
3601 typedef struct _ChangeData {
3602 MsgInfo *info;
3603 ChangeType op;
3604 MsgPermFlags set_flags;
3605 MsgTmpFlags set_tmp_flags;
3606 MsgPermFlags unset_flags;
3607 MsgTmpFlags unset_tmp_flags;
3608 } ChangeData;
3610 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3612 if (!msginfo->folder || !msginfo->folder->processing_pending) {
3613 debug_print("flags: doing unset now\n");
3614 procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags);
3615 } else {
3616 ChangeData *unset_data = g_new0(ChangeData, 1);
3617 unset_data->info = msginfo;
3618 unset_data->op = FLAGS_UNSET;
3619 unset_data->unset_flags = flags;
3620 unset_data->unset_tmp_flags = tmp_flags;
3621 debug_print("flags: deferring unset\n");
3622 g_timeout_add(100, defer_change, unset_data);
3626 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags)
3628 if (!msginfo->folder || !msginfo->folder->processing_pending) {
3629 debug_print("flags: doing set now\n");
3630 procmsg_msginfo_set_flags(msginfo, flags, tmp_flags);
3631 } else {
3632 ChangeData *set_data = g_new0(ChangeData, 1);
3633 set_data->info = msginfo;
3634 set_data->op = FLAGS_SET;
3635 set_data->set_flags = flags;
3636 set_data->set_tmp_flags = tmp_flags;
3637 debug_print("flags: deferring set\n");
3638 g_timeout_add(100, defer_change, set_data);
3642 static void summary_msginfo_change_flags(MsgInfo *msginfo,
3643 MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags,
3644 MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags)
3646 if (!msginfo->folder || !msginfo->folder->processing_pending) {
3647 debug_print("flags: doing change now\n");
3648 procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags,
3649 rem_flags, rem_tmp_flags);
3650 } else {
3651 ChangeData *change_data = g_new0(ChangeData, 1);
3652 change_data->info = msginfo;
3653 change_data->op = FLAGS_CHANGE;
3654 change_data->set_flags = add_flags;
3655 change_data->set_tmp_flags = add_tmp_flags;
3656 change_data->unset_flags = rem_flags;
3657 change_data->unset_tmp_flags = rem_tmp_flags;
3658 debug_print("flags: deferring change\n");
3659 g_timeout_add(100, defer_change, change_data);
3663 gboolean defer_change(gpointer data)
3665 ChangeData *chg = (ChangeData *)data;
3666 if (chg->info->folder && chg->info->folder->processing_pending) {
3667 debug_print("flags: trying later\n");
3668 return TRUE; /* try again */
3669 } else {
3670 debug_print("flags: finally doing it\n");
3671 switch(chg->op) {
3672 case FLAGS_UNSET:
3673 procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags);
3674 break;
3675 case FLAGS_SET:
3676 procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags);
3677 break;
3678 case FLAGS_CHANGE:
3679 procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags,
3680 chg->unset_flags, chg->unset_tmp_flags);
3681 break;
3682 default:
3683 g_warning("unknown change op");
3685 g_free(chg);
3687 return FALSE;
3690 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo,
3691 GtkCMCTreeNode *row)
3693 cm_return_if_fail(summaryview != NULL);
3694 cm_return_if_fail(msginfo != NULL);
3695 cm_return_if_fail(row != NULL);
3697 if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) {
3698 summary_msginfo_unset_flags
3699 (msginfo, MSG_NEW | MSG_UNREAD, 0);
3700 summary_set_row_marks(summaryview, row);
3701 summary_status_show(summaryview);
3705 typedef struct {
3706 MsgInfo *msginfo;
3707 SummaryView *summaryview;
3708 } MarkAsReadData;
3710 static int msginfo_mark_as_read_timeout(void *data)
3712 MarkAsReadData *mdata = (MarkAsReadData *)data;
3713 if (!mdata)
3714 return FALSE;
3716 summary_lock(mdata->summaryview);
3717 if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview))
3718 msginfo_mark_as_read(mdata->summaryview, mdata->msginfo,
3719 mdata->summaryview->selected);
3720 procmsg_msginfo_free(&(mdata->msginfo));
3722 mdata->summaryview->mark_as_read_timeout_tag = 0;
3723 summary_unlock(mdata->summaryview);
3725 g_free(mdata);
3726 return FALSE;
3729 static void summary_display_msg_full(SummaryView *summaryview,
3730 GtkCMCTreeNode *row,
3731 gboolean new_window, gboolean all_headers)
3733 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3734 MsgInfo *msginfo;
3735 gint val;
3736 START_TIMING("");
3737 if (!new_window) {
3738 if (summaryview->displayed == row &&
3739 messageview_is_visible(summaryview->messageview))
3740 return;
3741 else if (summaryview->messageview)
3742 summaryview->messageview->filtered = FALSE;
3744 cm_return_if_fail(row != NULL);
3746 if (summary_is_locked(summaryview)) return;
3747 summary_lock(summaryview);
3749 STATUSBAR_POP(summaryview->mainwin);
3750 GTK_EVENTS_FLUSH();
3752 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3754 if (!msginfo) {
3755 debug_print("NULL msginfo\n");
3756 summary_unlock(summaryview);
3757 END_TIMING();
3758 return;
3761 if (new_window && prefs_common.layout_mode != SMALL_LAYOUT) {
3762 MessageView *msgview;
3764 msgview = messageview_create_with_new_window(summaryview->mainwin);
3765 val = messageview_show(msgview, msginfo, all_headers);
3766 } else {
3767 MessageView *msgview;
3769 if (prefs_common.layout_mode == SMALL_LAYOUT) {
3770 if (summaryview->ext_messageview == NULL)
3771 summaryview->ext_messageview = messageview_create_with_new_window(summaryview->mainwin);
3772 else
3773 gtkut_window_popup(summaryview->ext_messageview->window);
3774 msgview = summaryview->ext_messageview;
3775 summaryview->displayed = row;
3776 val = messageview_show(msgview, msginfo, all_headers);
3777 if (mimeview_tree_is_empty(msgview->mimeview))
3778 gtk_widget_grab_focus(summaryview->ctree);
3779 gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3780 GTK_CMCLIST(summaryview->ctree)->focus_row);
3781 } else {
3782 msgview = summaryview->messageview;
3783 summaryview->displayed = row;
3784 if (!messageview_is_visible(msgview) &&
3785 gtk_window_is_active(GTK_WINDOW(summaryview->mainwin->window))) {
3786 main_window_toggle_message_view(summaryview->mainwin);
3787 GTK_EVENTS_FLUSH();
3789 val = messageview_show(msgview, msginfo, all_headers);
3790 if (mimeview_tree_is_empty(msgview->mimeview))
3791 gtk_widget_grab_focus(summaryview->ctree);
3792 gtkut_ctree_node_move_if_on_the_edge(ctree, row,
3793 GTK_CMCLIST(summaryview->ctree)->focus_row);
3797 if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) {
3798 if (!prefs_common.mark_as_read_on_new_window &&
3799 prefs_common.mark_as_read_delay) {
3800 MarkAsReadData *data = g_new0(MarkAsReadData, 1);
3801 data->summaryview = summaryview;
3802 data->msginfo = procmsg_msginfo_new_ref(msginfo);
3803 if (summaryview->mark_as_read_timeout_tag != 0)
3804 g_source_remove(summaryview->mark_as_read_timeout_tag);
3805 summaryview->mark_as_read_timeout_tag =
3806 g_timeout_add_seconds(prefs_common.mark_as_read_delay,
3807 msginfo_mark_as_read_timeout, data);
3808 } else if (new_window || !prefs_common.mark_as_read_on_new_window) {
3809 msginfo_mark_as_read(summaryview, msginfo, row);
3813 summary_set_menu_sensitive(summaryview);
3814 toolbar_main_set_sensitive(summaryview->mainwin);
3815 messageview_set_menu_sensitive(summaryview->messageview);
3817 summary_unlock(summaryview);
3818 END_TIMING();
3821 void summary_display_msg_selected(SummaryView *summaryview,
3822 gboolean all_headers)
3824 if (summary_is_locked(summaryview)) return;
3825 summaryview->displayed = NULL;
3826 summary_display_msg_full(summaryview, summaryview->selected, FALSE,
3827 all_headers);
3830 void summary_redisplay_msg(SummaryView *summaryview)
3832 GtkCMCTreeNode *node;
3834 if (summaryview->displayed) {
3835 node = summaryview->displayed;
3836 summaryview->displayed = NULL;
3837 summary_display_msg(summaryview, node);
3841 void summary_open_msg(SummaryView *summaryview)
3843 if (!summaryview->selected) return;
3845 /* CLAWS: if separate message view, don't open a new window
3846 * but rather use the current separated message view */
3847 summary_display_msg_full(summaryview, summaryview->selected,
3848 TRUE, FALSE);
3851 void summary_view_source(SummaryView * summaryview)
3853 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3854 MsgInfo *msginfo;
3855 SourceWindow *srcwin;
3857 if (!summaryview->selected) return;
3859 srcwin = source_window_create();
3860 msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
3861 source_window_show_msg(srcwin, msginfo);
3862 source_window_show(srcwin);
3865 void summary_reedit(SummaryView *summaryview)
3867 MsgInfo *msginfo;
3869 if (!summaryview->selected) return;
3870 if (!summaryview->folder_item) return;
3871 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
3872 return;
3874 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
3875 summaryview->selected);
3876 if (!msginfo) return;
3878 compose_reedit(msginfo, FALSE);
3881 gboolean summary_is_list(SummaryView *summaryview)
3883 return (gtk_notebook_get_current_page(
3884 GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0);
3887 void summary_toggle_view(SummaryView *summaryview)
3889 if (prefs_common.layout_mode == SMALL_LAYOUT)
3890 return;
3891 if (summary_is_locked(summaryview))
3892 return;
3893 if (!messageview_is_visible(summaryview->messageview) &&
3894 summaryview->selected && summary_is_list(summaryview))
3895 summary_display_msg(summaryview,
3896 summaryview->selected);
3897 else
3898 main_window_toggle_message_view(summaryview->mainwin);
3901 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree,
3902 GtkCMCTreeNode *node)
3904 MsgInfo *msginfo;
3906 if (node) {
3907 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
3908 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3909 return TRUE;
3910 node = GTK_CMCTREE_ROW(node)->children;
3911 } else
3912 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
3914 while (node) {
3915 if (summary_search_unread_recursive(ctree, node) == TRUE)
3916 return TRUE;
3917 node = GTK_CMCTREE_ROW(node)->sibling;
3920 return FALSE;
3923 static gboolean summary_have_unread_children(SummaryView *summaryview,
3924 GtkCMCTreeNode *node)
3926 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3928 if (!node) return FALSE;
3930 node = GTK_CMCTREE_ROW(node)->children;
3932 while (node) {
3933 if (summary_search_unread_recursive(ctree, node) == TRUE)
3934 return TRUE;
3935 node = GTK_CMCTREE_ROW(node)->sibling;
3937 return FALSE;
3940 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row)
3942 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
3943 GtkStyle *style = NULL;
3944 MsgInfo *msginfo;
3945 MsgFlags flags;
3946 gint *col_pos = summaryview->col_pos;
3948 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
3949 if (!msginfo) return;
3951 flags = msginfo->flags;
3953 gtk_cmctree_node_set_foreground(ctree, row, NULL);
3955 /* set new/unread column */
3956 if (MSG_IS_IGNORE_THREAD(flags)) {
3957 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3958 ignorethreadxpm);
3959 } else if (MSG_IS_WATCH_THREAD(flags)) {
3960 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3961 watchthreadxpm);
3962 } else if (MSG_IS_SPAM(flags)) {
3963 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3964 spamxpm);
3965 } else if (MSG_IS_NEW(flags)) {
3966 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3967 newxpm);
3968 } else if (MSG_IS_UNREAD(flags)) {
3969 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3970 unreadxpm);
3971 } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
3972 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3973 repliedandforwardedxpm);
3974 } else if (MSG_IS_REPLIED(flags)) {
3975 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3976 repliedxpm);
3977 } else if (MSG_IS_FORWARDED(flags)) {
3978 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS],
3979 forwardedxpm);
3980 } else {
3981 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS],
3982 "");
3985 if (prefs_common.bold_unread &&
3986 ((MSG_IS_UNREAD(flags) && !MSG_IS_IGNORE_THREAD(flags)) ||
3987 (!GTK_CMCTREE_ROW(row)->expanded &&
3988 GTK_CMCTREE_ROW(row)->children &&
3989 summary_have_unread_children(summaryview, row))))
3990 style = bold_style;
3992 /* set mark column */
3993 if (MSG_IS_DELETED(flags)) {
3994 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
3995 deletedxpm);
3996 if (style)
3997 style = bold_style;
3998 gtk_cmctree_node_set_foreground
3999 (ctree, row, &summaryview->color_dim);
4000 } else if (MSG_IS_MARKED(flags)) {
4001 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4002 markxpm);
4003 } else if (MSG_IS_MOVE(flags)) {
4004 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4005 movedxpm);
4006 if (!msginfo->to_folder ||
4007 !folder_has_parent_of_type(msginfo->to_folder, F_TRASH)) {
4008 if (style)
4009 style = bold_style;
4010 gtk_cmctree_node_set_foreground
4011 (ctree, row, &summaryview->color_marked);
4012 } else {
4013 if (style)
4014 style = bold_style;
4015 gtk_cmctree_node_set_foreground
4016 (ctree, row, &summaryview->color_dim);
4018 } else if (MSG_IS_COPY(flags)) {
4019 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK],
4020 copiedxpm);
4021 if (style)
4022 style = bold_style;
4023 gtk_cmctree_node_set_foreground
4024 (ctree, row, &summaryview->color_marked);
4025 } else {
4026 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], "");
4029 if (MSG_IS_LOCKED(flags)) {
4030 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_LOCKED],
4031 lockedxpm);
4033 else {
4034 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], "");
4037 if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
4038 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4039 clipgpgsignedxpm);
4040 } else if (MSG_IS_SIGNED(flags)) {
4041 if (MSG_IS_ENCRYPTED(flags)) {
4042 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4043 keysignxpm);
4044 } else {
4045 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4046 gpgsignedxpm);
4048 } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
4049 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4050 clipkeyxpm);
4051 } else if (MSG_IS_ENCRYPTED(flags)) {
4052 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4053 keyxpm);
4054 } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
4055 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME],
4056 clipxpm);
4057 } else {
4058 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], "");
4061 gtk_cmctree_node_set_row_style(ctree, row, style);
4063 if (MSG_GET_COLORLABEL(flags))
4064 summary_set_colorlabel_color(ctree, row, MSG_GET_COLORLABEL_VALUE(flags));
4067 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4069 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4070 MsgInfo *msginfo;
4072 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4073 cm_return_if_fail(msginfo);
4074 if (MSG_IS_DELETED(msginfo->flags))
4075 summaryview->deleted--;
4076 if (MSG_IS_MOVE(msginfo->flags))
4077 summaryview->moved--;
4078 if (MSG_IS_COPY(msginfo->flags))
4079 summaryview->copied--;
4081 procmsg_msginfo_set_to_folder(msginfo, NULL);
4082 summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED,
4083 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4084 summary_set_row_marks(summaryview, row);
4085 debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum);
4088 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4090 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4091 MsgInfo *msginfo;
4093 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4094 cm_return_if_fail(msginfo);
4095 if (MSG_IS_DELETED(msginfo->flags))
4096 summaryview->deleted--;
4097 if (MSG_IS_MOVE(msginfo->flags)) {
4098 summaryview->moved--;
4100 if (MSG_IS_COPY(msginfo->flags)) {
4101 summaryview->copied--;
4103 procmsg_msginfo_set_to_folder(msginfo, NULL);
4104 summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED,
4105 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4107 summary_set_row_marks(summaryview, row);
4108 debug_print("Message %d is locked\n", msginfo->msgnum);
4111 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4113 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4114 MsgInfo *msginfo;
4116 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4117 cm_return_if_fail(msginfo);
4118 if (!MSG_IS_LOCKED(msginfo->flags))
4119 return;
4120 procmsg_msginfo_set_to_folder(msginfo, NULL);
4121 summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0);
4122 summary_set_row_marks(summaryview, row);
4123 debug_print("Message %d is unlocked\n", msginfo->msgnum);
4126 void summary_mark(SummaryView *summaryview)
4128 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4129 GList *cur;
4130 gboolean froze = FALSE;
4132 if (summary_is_locked(summaryview))
4133 return;
4134 START_LONG_OPERATION(summaryview, FALSE);
4135 folder_item_set_batch(summaryview->folder_item, TRUE);
4136 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4137 summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4138 folder_item_set_batch(summaryview->folder_item, FALSE);
4139 END_LONG_OPERATION(summaryview);
4141 summary_status_show(summaryview);
4144 static void summary_mark_row_as_read(SummaryView *summaryview,
4145 GtkCMCTreeNode *row)
4147 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4148 MsgInfo *msginfo;
4150 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4151 cm_return_if_fail(msginfo);
4153 if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
4154 return;
4156 summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
4157 summary_set_row_marks(summaryview, row);
4158 debug_print("Message %d is marked as read\n",
4159 msginfo->msgnum);
4162 static void summary_mark_row_as_unread(SummaryView *summaryview,
4163 GtkCMCTreeNode *row)
4165 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4166 MsgInfo *msginfo;
4168 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4169 cm_return_if_fail(msginfo);
4171 if(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))
4172 return;
4174 summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0);
4175 summary_set_row_marks(summaryview, row);
4176 debug_print("Message %d is marked as unread\n",
4177 msginfo->msgnum);
4180 void summary_mark_as_read(SummaryView *summaryview)
4182 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4183 GList *cur;
4184 gboolean froze = FALSE;
4186 if (summary_is_locked(summaryview))
4187 return;
4189 if (!summaryview->folder_item)
4190 return;
4192 if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
4193 summaryview->folder_item->total_msgs > 1) &&
4194 !summary_mark_all_read_confirm(TRUE))
4195 return;
4197 START_LONG_OPERATION(summaryview, FALSE);
4198 folder_item_set_batch(summaryview->folder_item, TRUE);
4199 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4200 summary_mark_row_as_read(summaryview,
4201 GTK_CMCTREE_NODE(cur->data));
4202 folder_item_set_batch(summaryview->folder_item, FALSE);
4203 END_LONG_OPERATION(summaryview);
4205 summary_status_show(summaryview);
4208 void summary_mark_as_unread(SummaryView *summaryview)
4210 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4211 GList *cur;
4212 gboolean froze = FALSE;
4214 if (summary_is_locked(summaryview))
4215 return;
4217 if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) &&
4218 summaryview->folder_item->total_msgs > 1) &&
4219 !summary_mark_all_unread_confirm(TRUE))
4220 return;
4222 START_LONG_OPERATION(summaryview, FALSE);
4223 folder_item_set_batch(summaryview->folder_item, TRUE);
4224 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4225 summary_mark_row_as_unread(summaryview,
4226 GTK_CMCTREE_NODE(cur->data));
4227 folder_item_set_batch(summaryview->folder_item, FALSE);
4228 END_LONG_OPERATION(summaryview);
4230 summary_status_show(summaryview);
4233 void summary_msgs_lock(SummaryView *summaryview)
4235 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4236 GList *cur;
4237 gboolean froze = FALSE;
4239 if (summary_is_locked(summaryview))
4240 return;
4241 START_LONG_OPERATION(summaryview, FALSE);
4242 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4243 summary_lock_row(summaryview,
4244 GTK_CMCTREE_NODE(cur->data));
4245 END_LONG_OPERATION(summaryview);
4247 summary_status_show(summaryview);
4250 void summary_msgs_unlock(SummaryView *summaryview)
4252 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4253 GList *cur;
4254 gboolean froze = FALSE;
4256 if (summary_is_locked(summaryview))
4257 return;
4258 START_LONG_OPERATION(summaryview, FALSE);
4259 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4260 summary_unlock_row(summaryview,
4261 GTK_CMCTREE_NODE(cur->data));
4262 END_LONG_OPERATION(summaryview);
4264 summary_status_show(summaryview);
4267 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed)
4269 /* ask_if_needed is FALSE when user-asking is performed by caller,
4270 commonly when the caller is a mark-as-read-recursive func */
4271 if (ask_if_needed && prefs_common.ask_mark_all_read) {
4272 AlertValue val = alertpanel_full(_("Mark all as read"),
4273 _("Do you really want to mark all mails in this folder as read?"),
4274 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST,
4275 TRUE, NULL, ALERT_QUESTION);
4277 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4278 return FALSE;
4279 else if (val & G_ALERTDISABLE)
4280 prefs_common.ask_mark_all_read = FALSE;
4282 return TRUE;
4285 void summary_mark_all_read(SummaryView *summaryview, gboolean ask_if_needed)
4287 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4288 GtkCMCTreeNode *node;
4289 gboolean froze = FALSE;
4291 if (summary_is_locked(summaryview))
4292 return;
4294 if (!summary_mark_all_read_confirm(ask_if_needed))
4295 return;
4297 START_LONG_OPERATION(summaryview, TRUE);
4298 folder_item_set_batch(summaryview->folder_item, TRUE);
4299 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4300 node = gtkut_ctree_node_next(ctree, node))
4301 summary_mark_row_as_read(summaryview, node);
4302 folder_item_set_batch(summaryview->folder_item, FALSE);
4303 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4304 node = gtkut_ctree_node_next(ctree, node)) {
4305 if (!GTK_CMCTREE_ROW(node)->expanded)
4306 summary_set_row_marks(summaryview, node);
4308 END_LONG_OPERATION(summaryview);
4310 summary_status_show(summaryview);
4313 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed)
4315 /* ask_if_needed is FALSE when user-asking is performed by caller,
4316 commonly when the caller is a mark-as-unread-recursive func */
4317 if (ask_if_needed && prefs_common.ask_mark_all_read) {
4318 AlertValue val = alertpanel_full(_("Mark all as unread"),
4319 _("Do you really want to mark all mails in this folder as unread?"),
4320 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST,
4321 TRUE, NULL, ALERT_QUESTION);
4323 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
4324 return FALSE;
4325 else if (val & G_ALERTDISABLE)
4326 prefs_common.ask_mark_all_read = FALSE;
4328 return TRUE;
4331 void summary_mark_all_unread(SummaryView *summaryview, gboolean ask_if_needed)
4333 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4334 GtkCMCTreeNode *node;
4335 gboolean froze = FALSE;
4337 if (summary_is_locked(summaryview))
4338 return;
4340 if (!summary_mark_all_unread_confirm(ask_if_needed))
4341 return;
4343 START_LONG_OPERATION(summaryview, TRUE);
4344 folder_item_set_batch(summaryview->folder_item, TRUE);
4345 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4346 node = gtkut_ctree_node_next(ctree, node))
4347 summary_mark_row_as_unread(summaryview, node);
4348 folder_item_set_batch(summaryview->folder_item, FALSE);
4349 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL;
4350 node = gtkut_ctree_node_next(ctree, node)) {
4351 if (!GTK_CMCTREE_ROW(node)->expanded)
4352 summary_set_row_marks(summaryview, node);
4354 END_LONG_OPERATION(summaryview);
4356 summary_status_show(summaryview);
4359 void summary_mark_as_spam(SummaryView *summaryview, guint action, GtkWidget *widget)
4361 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4362 GList *cur;
4363 gboolean is_spam = action;
4364 GSList *msgs = NULL;
4365 gboolean immediate_exec = prefs_common.immediate_exec;
4366 gboolean moved = FALSE;
4367 gboolean froze = FALSE;
4370 if (summary_is_locked(summaryview))
4371 return;
4373 prefs_common.immediate_exec = FALSE;
4374 START_LONG_OPERATION(summaryview, FALSE);
4375 folder_item_set_batch(summaryview->folder_item, TRUE);
4376 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4377 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4378 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4379 if (msginfo)
4380 msgs = g_slist_prepend(msgs, msginfo);
4383 if (procmsg_spam_learner_learn(NULL, msgs, is_spam) == 0) {
4384 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4385 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4386 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4387 if (!msginfo)
4388 continue;
4389 if (is_spam) {
4390 summary_msginfo_change_flags(msginfo, MSG_SPAM, 0, MSG_NEW|MSG_UNREAD, 0);
4391 if (procmsg_spam_get_folder(msginfo) != summaryview->folder_item) {
4392 summary_move_row_to(summaryview, row,
4393 procmsg_spam_get_folder(msginfo));
4394 moved = TRUE;
4396 } else {
4397 summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
4399 summaryview->display_msg = prefs_common.always_show_msg;
4401 summary_set_row_marks(summaryview, row);
4403 } else {
4404 log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
4407 prefs_common.immediate_exec = immediate_exec;
4408 folder_item_set_batch(summaryview->folder_item, FALSE);
4409 END_LONG_OPERATION(summaryview);
4411 if (prefs_common.immediate_exec && moved) {
4412 summary_execute(summaryview);
4415 if (!moved && msgs) {
4416 MsgInfo *msginfo = (MsgInfo *)msgs->data;
4417 toolbar_set_learn_button
4418 (summaryview->mainwin->toolbar,
4419 MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
4421 g_slist_free(msgs);
4423 summary_status_show(summaryview);
4426 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo)
4428 GList * cur;
4429 gboolean found;
4431 switch (FOLDER_TYPE(summaryview->folder_item->folder)) {
4433 case F_NEWS:
4436 security : checks if one the accounts correspond to
4437 the author of the post
4440 found = FALSE;
4441 for(cur = account_get_list() ; cur != NULL ; cur = cur->next) {
4442 PrefsAccount * account;
4443 gchar * from_name;
4445 account = cur->data;
4446 if (account->name && *account->name)
4447 from_name =
4448 g_strdup_printf("%s <%s>",
4449 account->name,
4450 account->address);
4451 else
4452 from_name =
4453 g_strdup_printf("%s",
4454 account->address);
4456 if (g_utf8_collate(from_name, msginfo->from) == 0) {
4457 g_free(from_name);
4458 found = TRUE;
4459 break;
4461 g_free(from_name);
4464 if (!found) {
4465 alertpanel_error(_("You're not the author of the article."));
4468 return found;
4470 default:
4471 return TRUE;
4475 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4477 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4478 MsgInfo *msginfo;
4480 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4481 cm_return_if_fail(msginfo);
4483 if (MSG_IS_LOCKED(msginfo->flags)) return;
4485 if (MSG_IS_DELETED(msginfo->flags)) return;
4487 if (MSG_IS_MOVE(msginfo->flags))
4488 summaryview->moved--;
4489 if (MSG_IS_COPY(msginfo->flags))
4490 summaryview->copied--;
4492 procmsg_msginfo_set_to_folder(msginfo, NULL);
4493 summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED,
4494 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4495 summaryview->deleted++;
4497 if (!prefs_common.immediate_exec &&
4498 !folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) {
4499 summary_set_row_marks(summaryview, row);
4501 debug_print("Message %s/%d is set to delete\n",
4502 msginfo->folder->path, msginfo->msgnum);
4505 void summary_cancel(SummaryView *summaryview)
4507 MsgInfo * msginfo;
4509 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4510 summaryview->selected);
4511 if (!msginfo) return;
4513 if (!check_permission(summaryview, msginfo))
4514 return;
4516 news_cancel_article(summaryview->folder_item->folder, msginfo);
4518 if (summary_is_locked(summaryview)) return;
4520 summary_lock(summaryview);
4522 summary_freeze(summaryview);
4524 summary_update_status(summaryview);
4525 summary_status_show(summaryview);
4527 summary_thaw(summaryview);
4529 summary_unlock(summaryview);
4532 void summary_delete(SummaryView *summaryview)
4534 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4535 FolderItem *item = summaryview->folder_item;
4536 GList *cur;
4537 GtkCMCTreeNode *sel_last = NULL;
4538 GtkCMCTreeNode *node;
4539 AlertValue aval;
4540 MsgInfo *msginfo;
4541 gboolean froze = FALSE;
4543 if (!item) return;
4545 if (summary_is_locked(summaryview)) return;
4547 if (!summaryview->folder_item) return;
4549 START_LONG_OPERATION(summaryview, FALSE);
4551 if (!prefs_common.live_dangerously) {
4552 gchar *buf = NULL;
4553 guint num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
4554 buf = g_strdup_printf(ngettext(
4555 "Do you really want to delete the selected message?",
4556 "Do you really want to delete the %d selected messages?", num),
4557 num);
4558 aval = alertpanel(ngettext("Delete message", "Delete messages", num),
4559 buf,
4560 NULL, _("_Cancel"), NULL, _("_Delete"), NULL, NULL, ALERTFOCUS_SECOND);
4561 g_free(buf);
4562 if (aval != G_ALERTALTERNATE) {
4563 END_LONG_OPERATION(summaryview);
4564 return;
4568 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL;
4569 cur = cur->next) {
4570 GtkCMCTreeNode *row = GTK_CMCTREE_NODE(cur->data);
4571 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4572 if (msginfo && msginfo->total_size != 0 &&
4573 msginfo->size != (goffset)msginfo->total_size)
4574 partial_mark_for_delete(msginfo);
4577 main_window_cursor_wait(summaryview->mainwin);
4579 /* next code sets current row focus right. We need to find a row
4580 * that is not deleted. */
4581 folder_item_set_batch(summaryview->folder_item, TRUE);
4582 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
4583 sel_last = GTK_CMCTREE_NODE(cur->data);
4584 summary_delete_row(summaryview, sel_last);
4586 folder_item_set_batch(summaryview->folder_item, FALSE);
4587 END_LONG_OPERATION(summaryview);
4589 if (summaryview->sort_type == SORT_ASCENDING) {
4590 node = summary_find_next_msg(summaryview, sel_last, TRUE);
4591 if (!node || prefs_common.next_on_delete == FALSE)
4592 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4593 } else {
4594 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4595 if (!node || prefs_common.next_on_delete == FALSE)
4596 node = summary_find_next_msg(summaryview, sel_last,TRUE);
4598 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4600 if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) {
4601 summary_execute(summaryview);
4602 /* after deleting, the anchor may be at an invalid row
4603 * so reset it to the node we found earlier */
4604 gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node);
4605 } else
4606 summary_status_show(summaryview);
4609 main_window_cursor_normal(summaryview->mainwin);
4612 void summary_delete_trash(SummaryView *summaryview)
4614 FolderItem *to_folder = NULL;
4615 PrefsAccount *ac;
4616 if (!summaryview->folder_item ||
4617 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4619 if (NULL != (ac = account_find_from_item(summaryview->folder_item)))
4620 to_folder = account_get_special_folder(ac, F_TRASH);
4622 if (to_folder == NULL)
4623 to_folder = summaryview->folder_item->folder->trash;
4625 if (to_folder == NULL || to_folder == summaryview->folder_item
4626 || folder_has_parent_of_type(summaryview->folder_item, F_TRASH))
4627 summary_delete(summaryview);
4628 else
4629 summary_move_selected_to(summaryview, to_folder);
4633 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row)
4635 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4636 MsgInfo *msginfo;
4638 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4639 cm_return_if_fail(msginfo);
4640 if (MSG_IS_DELETED(msginfo->flags))
4641 summaryview->deleted--;
4642 if (MSG_IS_MOVE(msginfo->flags))
4643 summaryview->moved--;
4644 if (MSG_IS_COPY(msginfo->flags))
4645 summaryview->copied--;
4647 procmsg_msginfo_set_to_folder(msginfo, NULL);
4648 summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED,
4649 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
4650 summary_set_row_marks(summaryview, row);
4652 debug_print("Message %s/%d is unmarked\n",
4653 msginfo->folder->path, msginfo->msgnum);
4656 void summary_unmark(SummaryView *summaryview)
4658 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4659 GList *cur;
4660 gboolean froze = FALSE;
4662 if (summary_is_locked(summaryview))
4663 return;
4664 START_LONG_OPERATION(summaryview, FALSE);
4665 folder_item_set_batch(summaryview->folder_item, TRUE);
4666 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
4667 summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data));
4668 folder_item_set_batch(summaryview->folder_item, FALSE);
4669 END_LONG_OPERATION(summaryview);
4671 summary_status_show(summaryview);
4674 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4675 FolderItem *to_folder)
4677 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4678 MsgInfo *msginfo;
4680 cm_return_if_fail(to_folder != NULL);
4682 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4683 cm_return_if_fail(msginfo);
4684 if (MSG_IS_LOCKED(msginfo->flags))
4685 return;
4687 procmsg_msginfo_set_to_folder(msginfo, to_folder);
4688 if (MSG_IS_DELETED(msginfo->flags))
4689 summaryview->deleted--;
4690 if (MSG_IS_COPY(msginfo->flags)) {
4691 summaryview->copied--;
4693 if (!MSG_IS_MOVE(msginfo->flags)) {
4694 summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED,
4695 MSG_COPY | MSG_MOVE_DONE);
4696 summaryview->moved++;
4697 } else {
4698 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY);
4701 if (!prefs_common.immediate_exec) {
4702 summary_set_row_marks(summaryview, row);
4705 debug_print("Message %d is set to move to %s\n",
4706 msginfo->msgnum, to_folder->path);
4709 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4711 GList *cur;
4712 GtkCMCTreeNode *sel_last = NULL;
4713 gboolean froze = FALSE;
4715 if (!to_folder) return;
4716 if (!summaryview->folder_item ||
4717 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4719 if (summary_is_locked(summaryview)) return;
4721 if (summaryview->folder_item == to_folder) {
4722 alertpanel_error(_("Destination is same as current folder."));
4723 return;
4726 if (to_folder->no_select) {
4727 alertpanel_error(_("The destination folder can only be used to "
4728 "store subfolders."));
4729 return;
4732 START_LONG_OPERATION(summaryview, FALSE);
4734 for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4735 cur != NULL && cur->data != NULL; cur = cur->next) {
4736 sel_last = GTK_CMCTREE_NODE(cur->data);
4737 summary_move_row_to
4738 (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4740 END_LONG_OPERATION(summaryview);
4742 if (prefs_common.immediate_exec) {
4743 summary_execute(summaryview);
4744 } else {
4745 GtkCMCTreeNode *node = NULL;
4746 if (summaryview->sort_type == SORT_ASCENDING) {
4747 node = summary_find_next_msg(summaryview, sel_last,TRUE);
4748 if (!node || prefs_common.next_on_delete == FALSE)
4749 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4750 } else {
4751 node = summary_find_prev_msg(summaryview, sel_last,TRUE);
4752 if (!node || prefs_common.next_on_delete == FALSE)
4753 node = summary_find_next_msg(summaryview, sel_last,TRUE);
4755 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4756 summary_status_show(summaryview);
4759 if (!summaryview->selected) { /* this was the last message */
4760 GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree),
4761 GTK_CMCLIST(summaryview->ctree)->rows - 1);
4762 if (node)
4763 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE);
4768 void summary_move_to(SummaryView *summaryview)
4770 FolderItem *to_folder;
4772 if (!summaryview->folder_item ||
4773 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return;
4775 to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE,
4776 ngettext(
4777 "Select folder to move selected message to",
4778 "Select folder to move selected messages to",
4779 g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4781 summary_move_selected_to(summaryview, to_folder);
4784 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row,
4785 FolderItem *to_folder)
4787 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4788 MsgInfo *msginfo;
4790 cm_return_if_fail(to_folder != NULL);
4792 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
4793 cm_return_if_fail(msginfo);
4794 procmsg_msginfo_set_to_folder(msginfo, to_folder);
4795 if (MSG_IS_DELETED(msginfo->flags))
4796 summaryview->deleted--;
4797 if (MSG_IS_MOVE(msginfo->flags)) {
4798 summaryview->moved--;
4801 if (!MSG_IS_COPY(msginfo->flags)) {
4802 summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED,
4803 MSG_MOVE | MSG_MOVE_DONE);
4804 summaryview->copied++;
4805 } else {
4806 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE);
4808 if (!prefs_common.immediate_exec) {
4809 summary_set_row_marks(summaryview, row);
4812 debug_print("Message %d is set to copy to %s\n",
4813 msginfo->msgnum, to_folder->path);
4816 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder)
4818 GList *cur;
4819 gboolean froze = FALSE;
4821 if (!to_folder) return;
4822 if (!summaryview->folder_item) return;
4824 if (summary_is_locked(summaryview)) return;
4826 if (summaryview->folder_item == to_folder) {
4827 alertpanel_error
4828 (_("Destination to copy is same as current folder."));
4829 return;
4832 if (to_folder->no_select) {
4833 alertpanel_error(_("The destination folder can only be used to "
4834 "store subfolders."));
4835 return;
4838 START_LONG_OPERATION(summaryview, FALSE);
4840 for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
4841 cur != NULL && cur->data != NULL; cur = cur->next)
4842 summary_copy_row_to
4843 (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder);
4845 END_LONG_OPERATION(summaryview);
4847 if (prefs_common.immediate_exec)
4848 summary_execute(summaryview);
4849 else {
4850 summary_status_show(summaryview);
4854 void summary_copy_to(SummaryView *summaryview)
4856 FolderItem *to_folder;
4858 if (!summaryview->folder_item) return;
4860 to_folder = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE,
4861 ngettext(
4862 "Select folder to copy selected message to",
4863 "Select folder to copy selected messages to",
4864 g_list_length(GTK_CMCLIST(summaryview->ctree)->selection))
4866 summary_copy_selected_to(summaryview, to_folder);
4869 void summary_add_address(SummaryView *summaryview)
4871 MsgInfo *msginfo, *full_msginfo;
4872 gchar *from;
4873 GdkPixbuf *picture = NULL;
4874 AvatarRender *avatarr;
4876 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
4877 summaryview->selected);
4878 if (!msginfo || !msginfo->from)
4879 return;
4881 Xstrdup_a(from, msginfo->from, return);
4882 eliminate_address_comment(from);
4883 extract_address(from);
4885 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
4887 avatarr = avatars_avatarrender_new(full_msginfo);
4888 hooks_invoke(AVATAR_IMAGE_RENDER_HOOKLIST, avatarr);
4890 procmsg_msginfo_free(&full_msginfo);
4892 if (avatarr->image)
4893 picture = gtk_image_get_pixbuf(GTK_IMAGE(avatarr->image));
4895 #ifndef USE_ALT_ADDRBOOK
4896 addressbook_add_contact(msginfo->fromname, from, NULL, picture);
4897 #else
4898 if (addressadd_selection(msginfo->fromname, from, NULL, picture)) {
4899 debug_print( "addressbook_add_contact - added\n" );
4901 #endif
4902 avatars_avatarrender_free(avatarr);
4905 void summary_select_all(SummaryView *summaryview)
4907 GtkCMCTreeNode *node;
4909 if (!summaryview->folder_item) return;
4911 if (GTK_CMCLIST(summaryview->ctree)->focus_row < 0) {
4912 /* If no row is selected, select (but do not open) the first
4913 * row, to get summaryview into correct state for selecting all. */
4914 debug_print("summary_select_all: no row selected, selecting first one\n");
4915 if (GTK_CMCLIST(summaryview->ctree)->row_list != NULL) {
4916 node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
4917 summary_select_node(summaryview, node, FALSE);
4921 /* Now select all rows while locking the summaryview for
4922 * faster performance. */
4923 summary_lock(summaryview);
4924 gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree));
4925 summary_unlock(summaryview);
4927 summary_status_show(summaryview);
4930 void summary_unselect_all(SummaryView *summaryview)
4932 summary_lock(summaryview);
4933 gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree));
4934 summary_unlock(summaryview);
4935 summary_status_show(summaryview);
4938 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread,
4939 gboolean trash_thread)
4941 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4942 GtkCMCTreeNode *node = NULL;
4943 gboolean froze = FALSE;
4944 GList *cur = NULL;
4945 GList *copy = NULL;
4946 if (!GTK_CMCLIST(summaryview->ctree)->selection)
4947 return;
4950 START_LONG_OPERATION(summaryview, FALSE);
4951 copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection);
4952 for (cur = copy; cur != NULL && cur->data != NULL;
4953 cur = cur->next) {
4954 node = GTK_CMCTREE_NODE(cur->data);
4955 if (!node)
4956 continue;
4957 while (GTK_CMCTREE_ROW(node)->parent != NULL)
4958 node = GTK_CMCTREE_ROW(node)->parent;
4960 gtk_cmctree_select_recursive(ctree, node);
4962 g_list_free(copy);
4963 END_LONG_OPERATION(summaryview);
4965 if (trash_thread) {
4966 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS)
4967 summary_delete(summaryview);
4968 else
4969 summary_delete_trash(summaryview);
4970 } else if (delete_thread)
4971 summary_delete(summaryview);
4973 summary_status_show(summaryview);
4976 void summary_save_as(SummaryView *summaryview)
4978 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
4979 MsgInfo *msginfo;
4980 gchar *filename = NULL;
4981 gchar *src, *dest;
4982 gchar *tmp;
4983 gchar *filedir = NULL;
4984 gchar *converted_filename = NULL;
4985 gchar * filepath = NULL;
4987 AlertValue aval = 0;
4989 if (!summaryview->selected) return;
4990 msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected);
4991 if (!msginfo) return;
4993 if (msginfo->subject) {
4994 Xstrdup_a(filename, msginfo->subject, return);
4995 subst_for_filename(filename);
4998 manage_window_focus_in(summaryview->window, NULL, NULL);
5000 if (filename && !g_utf8_validate(filename, -1, NULL)) {
5001 converted_filename = conv_codeset_strdup(filename,
5002 conv_get_locale_charset_str(),
5003 CS_UTF_8);
5004 if (!converted_filename) {
5005 g_warning("summary_save_as(): failed to convert character set");
5006 } else {
5007 filename = converted_filename;
5010 if (!filename)
5011 return;
5013 if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir) {
5014 filepath = g_strconcat(prefs_common.attach_save_dir, G_DIR_SEPARATOR_S,
5015 filename, NULL);
5017 dest = filesel_select_file_save(_("Save as"), filepath ? filepath : filename);
5018 if (filepath)
5019 g_free(filepath);
5020 if (converted_filename)
5021 g_free(converted_filename);
5022 if (!dest)
5023 return;
5025 if (is_file_exist(dest)) {
5026 aval = alertpanel(_("Append or Overwrite"),
5027 _("Append or overwrite existing file?"),
5028 NULL, _("_Append"), NULL, _("_Overwrite"),
5029 NULL, _("_Cancel"), ALERTFOCUS_FIRST);
5030 if (aval != 0 && aval != 1)
5031 return;
5034 src = procmsg_get_message_file(msginfo);
5035 tmp = g_path_get_basename(dest);
5037 if ( aval==0 ) { /* append */
5038 if (append_file(src, dest, TRUE) < 0)
5039 alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5040 } else { /* overwrite */
5041 if (copy_file(src, dest, TRUE) < 0)
5042 alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5044 g_free(src);
5047 * If two or more msgs are selected,
5048 * append them to the output file.
5050 if (GTK_CMCLIST(ctree)->selection->next) {
5051 GList *item;
5052 for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) {
5053 msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data));
5054 if (!msginfo) break;
5055 src = procmsg_get_message_file(msginfo);
5056 if (append_file(src, dest, TRUE) < 0) {
5057 alertpanel_error(_("Couldn't save the file '%s'."), tmp);
5059 g_free(src);
5063 filedir = g_path_get_dirname(dest);
5064 if (filedir) {
5065 if (strcmp(filedir, ".")) {
5066 g_free(prefs_common.attach_save_dir);
5067 prefs_common.attach_save_dir = g_filename_to_utf8(filedir, -1, NULL, NULL, NULL);
5069 g_free(filedir);
5072 g_free(dest);
5073 g_free(tmp);
5076 void summary_print(SummaryView *summaryview)
5078 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5079 GList *cur;
5080 gchar *msg = g_strdup_printf(_("You are about to print %d "
5081 "messages, one by one. Do you "
5082 "want to continue?"),
5083 g_list_length(clist->selection));
5084 if (g_list_length(clist->selection) > 9
5085 && alertpanel(_("Warning"), msg, NULL, _("_Cancel"), NULL, _("_Yes"),
5086 NULL, NULL, ALERTFOCUS_SECOND) != G_ALERTALTERNATE) {
5087 g_free(msg);
5088 return;
5090 g_free(msg);
5092 if (clist->selection == NULL) return;
5093 for (cur = clist->selection;
5094 cur != NULL && cur->data != NULL;
5095 cur = cur->next) {
5096 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(cur->data);
5097 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(
5098 GTK_CMCTREE(summaryview->ctree),
5099 node);
5100 gint sel_start = -1, sel_end = -1, partnum = 0;
5102 if (node == summaryview->displayed) {
5103 partnum = mimeview_get_selected_part_num(summaryview->messageview->mimeview);
5104 textview_get_selection_offsets(summaryview->messageview->mimeview->textview,
5105 &sel_start, &sel_end);
5107 messageview_print(msginfo, summaryview->messageview->all_headers,
5108 sel_start, sel_end, partnum);
5112 gboolean summary_execute(SummaryView *summaryview)
5114 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5115 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5116 GtkCMCTreeNode *node, *next;
5117 GtkCMCTreeNode *new_selected = NULL;
5118 gint move_val = -1;
5120 if (!summaryview->folder_item) return FALSE;
5122 if (summary_is_locked(summaryview)) return FALSE;
5123 summary_lock(summaryview);
5125 summary_freeze(summaryview);
5127 main_window_cursor_wait(summaryview->mainwin);
5129 if (summaryview->threaded)
5130 summary_unthread_for_exec(summaryview);
5132 folder_item_update_freeze();
5133 move_val = summary_execute_move(summaryview);
5134 summary_execute_copy(summaryview);
5135 summary_execute_delete(summaryview);
5137 node = GTK_CMCTREE_NODE(clist->row_list);
5138 for (; node != NULL; node = next) {
5139 next = gtkut_ctree_node_next(ctree, node);
5140 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5142 if (node == summaryview->displayed) {
5143 messageview_clear(summaryview->messageview);
5144 summary_cancel_mark_read_timeout(summaryview);
5145 summaryview->displayed = NULL;
5147 if (GTK_CMCTREE_ROW(node)->children != NULL) {
5148 next = NULL;
5149 if (GTK_CMCTREE_ROW(node)->sibling) {
5150 next = GTK_CMCTREE_ROW(node)->sibling;
5151 } else {
5152 GtkCMCTreeNode *parent = NULL;
5153 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5154 parent = GTK_CMCTREE_ROW(parent)->parent) {
5155 if (GTK_CMCTREE_ROW(parent)->sibling) {
5156 next = GTK_CMCTREE_ROW(parent)->sibling;
5162 if (!new_selected &&
5163 gtkut_ctree_node_is_selected(ctree, node)) {
5164 summary_unselect_all(summaryview);
5165 if (summaryview->sort_type == SORT_ASCENDING) {
5166 new_selected = summary_find_next_msg(summaryview, node,TRUE);
5167 if (!new_selected || prefs_common.next_on_delete == FALSE)
5168 new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5169 } else {
5170 new_selected = summary_find_prev_msg(summaryview, node,TRUE);
5171 if (!new_selected || prefs_common.next_on_delete == FALSE)
5172 new_selected = summary_find_next_msg(summaryview, node,TRUE);
5176 gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5179 folder_item_update_thaw();
5181 if (new_selected) {
5182 summary_unlock(summaryview);
5183 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5184 summary_lock(summaryview);
5187 if (summaryview->threaded) {
5188 summary_thread_build(summaryview);
5189 summary_thread_init(summaryview);
5192 summary_thaw(summaryview);
5194 summaryview->selected = clist->selection ?
5195 GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5197 if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5198 menu_set_insensitive_all
5199 (GTK_MENU_SHELL(summaryview->popupmenu));
5200 folderview_grab_focus(summaryview->folderview);
5201 } else {
5202 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5203 gtk_widget_grab_focus(summaryview->ctree);
5205 summary_update_status(summaryview);
5206 summary_status_show(summaryview);
5208 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5210 summary_unlock(summaryview);
5212 main_window_cursor_normal(summaryview->mainwin);
5214 if (move_val < 0)
5215 summary_show(summaryview, summaryview->folder_item, FALSE);
5216 return TRUE;
5219 gboolean summary_expunge(SummaryView *summaryview)
5221 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5222 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree);
5223 GtkCMCTreeNode *node, *next;
5224 GtkCMCTreeNode *new_selected = NULL;
5226 if (!summaryview->folder_item) return FALSE;
5228 if (summary_is_locked(summaryview)) return FALSE;
5230 summary_lock(summaryview);
5232 summary_freeze(summaryview);
5234 main_window_cursor_wait(summaryview->mainwin);
5236 if (summaryview->threaded)
5237 summary_unthread_for_exec(summaryview);
5239 folder_item_update_freeze();
5240 summary_execute_expunge(summaryview);
5242 node = GTK_CMCTREE_NODE(clist->row_list);
5243 for (; node != NULL; node = next) {
5244 next = gtkut_ctree_node_next(ctree, node);
5245 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue;
5247 if (node == summaryview->displayed) {
5248 messageview_clear(summaryview->messageview);
5249 summary_cancel_mark_read_timeout(summaryview);
5250 summaryview->displayed = NULL;
5252 if (GTK_CMCTREE_ROW(node)->children != NULL) {
5253 next = NULL;
5254 if (GTK_CMCTREE_ROW(node)->sibling) {
5255 next = GTK_CMCTREE_ROW(node)->sibling;
5256 } else {
5257 GtkCMCTreeNode *parent = NULL;
5258 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL;
5259 parent = GTK_CMCTREE_ROW(parent)->parent) {
5260 if (GTK_CMCTREE_ROW(parent)->sibling) {
5261 next = GTK_CMCTREE_ROW(parent)->sibling;
5267 if (!new_selected &&
5268 gtkut_ctree_node_is_selected(ctree, node)) {
5269 summary_unselect_all(summaryview);
5270 new_selected = summary_find_next_msg(summaryview, node,TRUE);
5271 if (!new_selected)
5272 new_selected = summary_find_prev_msg
5273 (summaryview, node,TRUE);
5276 gtk_sctree_remove_node((GtkSCTree *)ctree, node);
5279 folder_item_update_thaw();
5281 if (new_selected) {
5282 summary_unlock(summaryview);
5283 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE);
5284 summary_lock(summaryview);
5287 if (summaryview->threaded) {
5288 summary_thread_build(summaryview);
5289 summary_thread_init(summaryview);
5292 summary_thaw(summaryview);
5294 summaryview->selected = clist->selection ?
5295 GTK_CMCTREE_NODE(clist->selection->data) : NULL;
5297 if (!GTK_CMCLIST(summaryview->ctree)->row_list) {
5298 menu_set_insensitive_all
5299 (GTK_MENU_SHELL(summaryview->popupmenu));
5300 folderview_grab_focus(summaryview->folderview);
5301 } else {
5302 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE);
5303 gtk_widget_grab_focus(summaryview->ctree);
5306 summary_update_status(summaryview);
5307 summary_status_show(summaryview);
5309 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5311 summary_unlock(summaryview);
5313 main_window_cursor_normal(summaryview->mainwin);
5315 return TRUE;
5318 static gint summary_execute_move(SummaryView *summaryview)
5320 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5321 GSList *cur;
5322 gint val = -1;
5323 /* search moving messages and execute */
5325 gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func,
5326 summaryview);
5328 if (summaryview->mlist) {
5329 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5330 summaryview->msginfo_update_callback_id);
5331 val = procmsg_move_messages(summaryview->mlist);
5332 summaryview->msginfo_update_callback_id =
5333 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5334 summary_update_msg, (gpointer) summaryview);
5336 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5337 procmsg_msginfo_free((MsgInfo **)&(cur->data));
5340 g_slist_free(summaryview->mlist);
5341 summaryview->mlist = NULL;
5342 return val;
5344 return 0;
5347 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5348 gpointer data)
5350 SummaryView *summaryview = data;
5351 MsgInfo *msginfo;
5353 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5355 if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) {
5356 summaryview->mlist =
5357 g_slist_prepend(summaryview->mlist, msginfo);
5358 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5360 if (msginfo->msgid && *msginfo->msgid &&
5361 node == g_hash_table_lookup(summaryview->msgid_table,
5362 msginfo->msgid))
5363 g_hash_table_remove(summaryview->msgid_table,
5364 msginfo->msgid);
5365 if (prefs_common.thread_by_subject &&
5366 msginfo->subject && *msginfo->subject &&
5367 node == subject_table_lookup(summaryview->subject_table,
5368 msginfo->subject)) {
5369 subject_table_remove(summaryview->subject_table,
5370 msginfo->subject);
5375 static void summary_execute_copy(SummaryView *summaryview)
5377 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5379 /* search copying messages and execute */
5380 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5381 summaryview->msginfo_update_callback_id);
5382 gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func,
5383 summaryview);
5385 if (summaryview->mlist) {
5386 summaryview->mlist = g_slist_reverse(summaryview->mlist);
5387 procmsg_copy_messages(summaryview->mlist);
5389 g_slist_free(summaryview->mlist);
5390 summaryview->mlist = NULL;
5392 summaryview->msginfo_update_callback_id =
5393 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5394 summary_update_msg, (gpointer) summaryview);
5397 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5398 gpointer data)
5400 SummaryView *summaryview = data;
5401 MsgInfo *msginfo;
5403 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5405 if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) {
5406 summaryview->mlist =
5407 g_slist_prepend(summaryview->mlist, msginfo);
5409 summary_msginfo_unset_flags(msginfo, 0, MSG_COPY);
5410 summary_set_row_marks(summaryview, node);
5414 static void summary_execute_delete(SummaryView *summaryview)
5416 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5417 GSList *cur;
5419 /* search deleting messages and execute */
5420 gtk_cmctree_pre_recursive
5421 (ctree, NULL, summary_execute_delete_func, summaryview);
5423 if (!summaryview->mlist) return;
5425 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5426 summaryview->msginfo_update_callback_id);
5428 folder_item_remove_msgs(summaryview->folder_item,
5429 summaryview->mlist);
5431 summaryview->msginfo_update_callback_id =
5432 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5433 summary_update_msg, (gpointer) summaryview);
5435 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) {
5436 procmsg_msginfo_free((MsgInfo **)&(cur->data));
5439 g_slist_free(summaryview->mlist);
5440 summaryview->mlist = NULL;
5443 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5444 gpointer data)
5446 SummaryView *summaryview = data;
5447 MsgInfo *msginfo;
5449 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5451 if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5452 summaryview->mlist =
5453 g_slist_prepend(summaryview->mlist, msginfo);
5454 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5456 if (msginfo->msgid && *msginfo->msgid &&
5457 node == g_hash_table_lookup(summaryview->msgid_table,
5458 msginfo->msgid)) {
5459 g_hash_table_remove(summaryview->msgid_table,
5460 msginfo->msgid);
5462 if (prefs_common.thread_by_subject &&
5463 msginfo->subject && *msginfo->subject &&
5464 node == subject_table_lookup(summaryview->subject_table,
5465 msginfo->subject)) {
5466 subject_table_remove(summaryview->subject_table,
5467 msginfo->subject);
5472 static void summary_execute_expunge_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5473 gpointer data)
5475 SummaryView *summaryview = data;
5476 MsgInfo *msginfo;
5478 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5480 if (msginfo && MSG_IS_DELETED(msginfo->flags)) {
5481 summaryview->mlist =
5482 g_slist_prepend(summaryview->mlist, msginfo);
5483 gtk_cmctree_node_set_row_data(ctree, node, NULL);
5485 if (msginfo->msgid && *msginfo->msgid &&
5486 node == g_hash_table_lookup(summaryview->msgid_table,
5487 msginfo->msgid)) {
5488 g_hash_table_remove(summaryview->msgid_table,
5489 msginfo->msgid);
5491 if (prefs_common.thread_by_subject &&
5492 msginfo->subject && *msginfo->subject &&
5493 node == subject_table_lookup(summaryview->subject_table,
5494 msginfo->subject)) {
5495 subject_table_remove(summaryview->subject_table,
5496 msginfo->subject);
5501 static void summary_execute_expunge(SummaryView *summaryview)
5503 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5504 GSList *cur;
5506 gtk_cmctree_pre_recursive
5507 (ctree, NULL, summary_execute_expunge_func, summaryview);
5509 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST,
5510 summaryview->msginfo_update_callback_id);
5512 folder_item_expunge(summaryview->folder_item);
5514 summaryview->msginfo_update_callback_id =
5515 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST,
5516 summary_update_msg, (gpointer) summaryview);
5517 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next)
5518 procmsg_msginfo_free((MsgInfo **)&(cur->data));
5520 g_slist_free(summaryview->mlist);
5521 summaryview->mlist = NULL;
5524 /* thread functions */
5526 static void summary_thread_build(SummaryView *summaryview)
5528 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5529 GtkCMCTreeNode *node;
5530 GtkCMCTreeNode *next;
5531 GtkCMCTreeNode *parent;
5532 MsgInfo *msginfo;
5533 GSList *reflist;
5535 summary_lock(summaryview);
5537 debug_print("Building threads...\n");
5538 STATUSBAR_PUSH(summaryview->mainwin, _("Building threads..."));
5539 main_window_cursor_wait(summaryview->mainwin);
5541 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5542 G_CALLBACK(summary_tree_expanded), summaryview);
5543 summary_freeze(summaryview);
5545 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5546 while (node) {
5547 next = GTK_CMCTREE_ROW(node)->sibling;
5549 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5551 parent = NULL;
5553 if (msginfo && msginfo->inreplyto) {
5554 parent = g_hash_table_lookup(summaryview->msgid_table,
5555 msginfo->inreplyto);
5557 if (!parent && msginfo->references) {
5558 for (reflist = msginfo->references;
5559 reflist != NULL; reflist = reflist->next)
5560 if ((parent = g_hash_table_lookup
5561 (summaryview->msgid_table,
5562 reflist->data)))
5563 break;
5567 if (msginfo && prefs_common.thread_by_subject && parent == NULL) {
5568 parent = subject_table_lookup
5569 (summaryview->subject_table,
5570 msginfo->subject);
5573 if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) {
5574 gtk_cmctree_move(ctree, node, parent, NULL);
5577 node = next;
5580 gtkut_ctree_set_focus_row(ctree, summaryview->selected);
5582 summary_thaw(summaryview);
5583 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5584 G_CALLBACK(summary_tree_expanded), summaryview);
5586 debug_print("Building threads done.\n");
5587 STATUSBAR_POP(summaryview->mainwin);
5588 main_window_cursor_normal(summaryview->mainwin);
5590 summaryview->threaded = TRUE;
5592 summary_unlock(summaryview);
5595 static void summary_thread_init(SummaryView *summaryview)
5597 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5598 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5599 GtkCMCTreeNode *next;
5600 START_TIMING("");
5601 if (!summaryview->thread_collapsed) {
5602 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5603 G_CALLBACK(summary_tree_expanded), summaryview);
5604 while (node) {
5605 next = GTK_CMCTREE_ROW(node)->sibling;
5606 if (GTK_CMCTREE_ROW(node)->children)
5607 gtk_cmctree_expand_recursive(ctree, node);
5608 node = next;
5610 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5611 G_CALLBACK(summary_tree_expanded), summaryview);
5613 END_TIMING();
5616 static void summary_unthread_for_exec(SummaryView *summaryview)
5618 GtkCMCTreeNode *node;
5619 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5620 gboolean froze = FALSE;
5622 debug_print("Unthreading for execution...\n");
5624 START_LONG_OPERATION(summaryview, TRUE);
5625 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5626 node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) {
5627 summary_unthread_for_exec_func(ctree, node, summaryview);
5630 END_LONG_OPERATION(summaryview);
5632 debug_print("Unthreading for execution done.\n");
5635 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5636 gpointer data)
5638 MsgInfo *msginfo;
5639 GtkCMCTreeNode *top_parent;
5640 GtkCMCTreeNode *child;
5641 GtkCMCTreeNode *sibling;
5642 SummaryView * summaryview = (SummaryView *)data;
5643 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node);
5645 if (!msginfo ||
5646 (!MSG_IS_MOVE(msginfo->flags) &&
5647 !MSG_IS_DELETED(msginfo->flags)))
5648 return;
5649 child = GTK_CMCTREE_ROW(node)->children;
5650 if (!child) return;
5652 if (node == summaryview->selected)
5653 summaryview->selected = NULL;
5654 if (node == summaryview->displayed)
5655 summaryview->displayed = NULL;
5657 summary_cancel_mark_read_timeout(summaryview);
5659 for (top_parent = node;
5660 GTK_CMCTREE_ROW(top_parent)->parent != NULL;
5661 top_parent = GTK_CMCTREE_ROW(top_parent)->parent)
5663 sibling = GTK_CMCTREE_ROW(top_parent)->sibling;
5665 GTK_SCTREE(ctree)->sorting = TRUE;
5666 while (child != NULL) {
5667 GtkCMCTreeNode *next_child;
5668 MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child);
5670 next_child = GTK_CMCTREE_ROW(child)->sibling;
5672 if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) {
5673 gtk_cmctree_move(ctree, child,
5674 NULL,
5675 sibling);
5676 } else {
5677 if (child == summaryview->displayed) {
5678 messageview_clear(summaryview->messageview);
5679 summaryview->displayed = NULL;
5681 if (child == summaryview->selected) {
5682 messageview_clear(summaryview->messageview);
5683 summaryview->selected = NULL;
5686 child = next_child;
5688 GTK_SCTREE(ctree)->sorting = FALSE;
5691 void summary_expand_threads(SummaryView *summaryview)
5693 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5694 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5695 GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5697 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5698 G_CALLBACK(summary_tree_expanded), summaryview);
5699 summary_freeze(summaryview);
5700 GTK_SCTREE(ctree)->sorting = TRUE;
5702 while (node) {
5703 if (GTK_CMCTREE_ROW(node)->children) {
5704 gtk_cmctree_expand(ctree, node);
5705 summary_set_row_marks(summaryview, node);
5707 node = GTK_CMCTREE_NODE_NEXT(node);
5710 GTK_SCTREE(ctree)->sorting = FALSE;
5711 if (focus_node) {
5712 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5714 summary_thaw(summaryview);
5716 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5717 G_CALLBACK(summary_tree_expanded), summaryview);
5719 summaryview->thread_collapsed = FALSE;
5721 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5724 void summary_collapse_threads(SummaryView *summaryview)
5726 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
5727 GtkCMCTreeNode *node = NULL;
5728 GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row));
5730 g_signal_handlers_block_by_func(G_OBJECT(ctree),
5731 G_CALLBACK(summary_tree_collapsed), summaryview);
5732 summary_freeze(summaryview);
5733 GTK_SCTREE(ctree)->sorting = TRUE;
5735 node = focus_node;
5736 while (node && GTK_CMCTREE_ROW(node)->parent) {
5737 focus_node = node = GTK_CMCTREE_ROW(node)->parent;
5739 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DIRECTIONAL);
5740 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
5741 while (node) {
5742 if (GTK_CMCTREE_ROW(node)->children) {
5743 gtk_cmctree_collapse(ctree, node);
5744 summary_set_row_marks(summaryview, node);
5746 node = GTK_CMCTREE_ROW(node)->sibling;
5749 GTK_SCTREE(ctree)->sorting = FALSE;
5750 if (focus_node) {
5751 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node);
5753 GTK_SCTREE(ctree)->anchor_row =
5754 gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row);
5755 summary_thaw(summaryview);
5756 g_signal_handlers_unblock_by_func(G_OBJECT(ctree),
5757 G_CALLBACK(summary_tree_collapsed), summaryview);
5759 summaryview->thread_collapsed = TRUE;
5761 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0);
5764 static void account_rules_radio_button_toggled_cb(GtkToggleButton *btn, gpointer data)
5766 prefs_common.apply_per_account_filtering_rules = GPOINTER_TO_INT(data);
5769 static gboolean summary_filter_get_mode(void)
5770 /* ask what to do w/ them: skip them, apply them regardless to the account,
5771 use the current account */
5773 /* TODO: eventually also propose to use the current folder's default account,
5774 if it is set */
5775 /* TODO: eventually allow to select the account to use from a optmenu */
5777 GtkWidget *vbox;
5778 GtkWidget *account_rules_skip;
5779 GtkWidget *account_rules_force;
5780 GtkWidget *account_rules_user_current;
5781 AlertValue val;
5783 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
5785 account_rules_skip = gtk_radio_button_new_with_label
5786 (NULL, _("Skip these rules"));
5787 account_rules_force = gtk_radio_button_new_with_label_from_widget
5788 (GTK_RADIO_BUTTON(account_rules_skip),
5789 _("Apply these rules regardless of the account they belong to"));
5790 account_rules_user_current = gtk_radio_button_new_with_label_from_widget
5791 (GTK_RADIO_BUTTON(account_rules_skip),
5792 _("Apply these rules if they apply to the current account"));
5793 gtk_box_pack_start (GTK_BOX (vbox), account_rules_skip, FALSE, FALSE, 0);
5794 gtk_box_pack_start (GTK_BOX (vbox), account_rules_force, FALSE, FALSE, 0);
5795 gtk_box_pack_start (GTK_BOX (vbox), account_rules_user_current, FALSE, FALSE, 0);
5796 g_signal_connect(G_OBJECT(account_rules_skip), "toggled",
5797 G_CALLBACK(account_rules_radio_button_toggled_cb),
5798 GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_SKIP));
5799 g_signal_connect(G_OBJECT(account_rules_force), "toggled",
5800 G_CALLBACK(account_rules_radio_button_toggled_cb),
5801 GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_FORCE));
5802 g_signal_connect(G_OBJECT(account_rules_user_current), "toggled",
5803 G_CALLBACK(account_rules_radio_button_toggled_cb),
5804 GINT_TO_POINTER(FILTERING_ACCOUNT_RULES_USE_CURRENT));
5805 switch (prefs_common.apply_per_account_filtering_rules) {
5806 case FILTERING_ACCOUNT_RULES_SKIP:
5807 gtk_toggle_button_set_active(
5808 GTK_TOGGLE_BUTTON(account_rules_skip), TRUE);
5809 break;
5810 case FILTERING_ACCOUNT_RULES_FORCE:
5811 gtk_toggle_button_set_active(
5812 GTK_TOGGLE_BUTTON(account_rules_force), TRUE);
5813 break;
5814 case FILTERING_ACCOUNT_RULES_USE_CURRENT:
5815 gtk_toggle_button_set_active(
5816 GTK_TOGGLE_BUTTON(account_rules_user_current), TRUE);
5817 break;
5820 val = alertpanel_with_widget(
5821 _("Filtering"),
5822 _("There are some filtering rules that belong to an account.\n"
5823 "Please choose what to do with these rules:"),
5824 NULL, _("_Cancel"), NULL, _("_Filter"), NULL, NULL,
5825 ALERTFOCUS_SECOND, TRUE, vbox);
5827 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) {
5828 return FALSE;
5829 } else if (val & G_ALERTDISABLE)
5830 prefs_common.ask_apply_per_account_filtering_rules = FALSE;
5832 return TRUE;
5835 void summary_filter(SummaryView *summaryview, gboolean selected_only)
5837 GSList *mlist = NULL, *cur_list;
5838 PrefsAccount *ac_prefs = NULL;
5839 summary_lock(summaryview);
5841 /* are there any per-account filtering rules? */
5842 if (prefs_common.ask_apply_per_account_filtering_rules == TRUE &&
5843 filtering_peek_per_account_rules(filtering_rules)) {
5845 if (summary_filter_get_mode() == FALSE) {
5846 summary_unlock(summaryview);
5847 return;
5851 folder_item_update_freeze();
5853 debug_print("filtering...\n");
5854 STATUSBAR_PUSH(summaryview->mainwin, _("Filtering..."));
5855 main_window_cursor_wait(summaryview->mainwin);
5857 summary_freeze(summaryview);
5859 if (selected_only) {
5860 GList *cur;
5862 for (cur = GTK_CMCLIST(summaryview->ctree)->selection;
5863 cur != NULL && cur->data != NULL; cur = cur->next) {
5864 mlist = g_slist_prepend(mlist,
5865 procmsg_msginfo_new_ref(
5866 GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data)));
5868 mlist = g_slist_reverse(mlist);
5869 } else {
5870 mlist = folder_item_get_msg_list(summaryview->folder_item);
5873 ac_prefs = ((summaryview->folder_item->folder != NULL) &&
5874 (summaryview->folder_item->folder->account != NULL))
5875 ? summaryview->folder_item->folder->account : NULL;
5877 folder_item_set_batch(summaryview->folder_item, TRUE);
5878 for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5879 summary_filter_func((MsgInfo *)cur_list->data, ac_prefs);
5881 folder_item_set_batch(summaryview->folder_item, FALSE);
5883 filtering_move_and_copy_msgs(mlist);
5885 for (cur_list = mlist; cur_list; cur_list = cur_list->next) {
5886 procmsg_msginfo_free((MsgInfo **)&(cur_list->data));
5888 g_slist_free(mlist);
5890 summary_thaw(summaryview);
5892 folder_item_update_thaw();
5893 debug_print("filtering done.\n");
5894 STATUSBAR_POP(summaryview->mainwin);
5895 main_window_cursor_normal(summaryview->mainwin);
5897 summary_unlock(summaryview);
5900 * CLAWS: summary_show() only valid after having a lock. ideally
5901 * we want the lock to be context aware...
5903 summary_show(summaryview, summaryview->folder_item, TRUE);
5906 static void summary_filter_func(MsgInfo *msginfo, PrefsAccount *ac_prefs)
5908 MailFilteringData mail_filtering_data;
5910 mail_filtering_data.msginfo = msginfo;
5911 mail_filtering_data.msglist = NULL;
5912 mail_filtering_data.filtered = NULL;
5913 mail_filtering_data.unfiltered = NULL;
5914 if (hooks_invoke(MAIL_MANUAL_FILTERING_HOOKLIST, &mail_filtering_data))
5915 return;
5917 filter_message_by_msginfo(filtering_rules, msginfo, ac_prefs,
5918 FILTERING_MANUALLY, NULL);
5921 void summary_msginfo_filter_open(FolderItem * item, MsgInfo *msginfo,
5922 PrefsFilterType type, gint processing_rule)
5924 gchar *header = NULL;
5925 gchar *key = NULL;
5927 procmsg_get_filter_keyword(msginfo, &header, &key, type);
5929 if (processing_rule) {
5930 if (item == NULL)
5931 prefs_filtering_open(&pre_global_processing,
5932 _("Processing rules to apply before folder rules"),
5933 MANUAL_ANCHOR_PROCESSING,
5934 header, key, FALSE);
5935 else
5936 prefs_filtering_open(&item->prefs->processing,
5937 _("Processing configuration"),
5938 MANUAL_ANCHOR_PROCESSING,
5939 header, key, FALSE);
5941 else {
5942 prefs_filtering_open(&filtering_rules,
5943 _("Filtering configuration"),
5944 MANUAL_ANCHOR_FILTERING,
5945 header, key, TRUE);
5948 g_free(header);
5949 g_free(key);
5952 void summary_filter_open(SummaryView *summaryview, PrefsFilterType type,
5953 gint processing_rule)
5955 MsgInfo *msginfo;
5956 FolderItem * item;
5958 if (!summaryview->selected) return;
5960 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
5961 summaryview->selected);
5962 if (!msginfo) return;
5964 item = summaryview->folder_item;
5965 summary_msginfo_filter_open(item, msginfo, type, processing_rule);
5968 /* color label */
5970 #define N_COLOR_LABELS colorlabel_get_color_count()
5972 static void summary_colorlabel_menu_item_activate_cb(GtkWidget *widget,
5973 gpointer data)
5975 guint color = GPOINTER_TO_UINT(data);
5976 SummaryView *summaryview;
5978 summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
5979 cm_return_if_fail(summaryview != NULL);
5981 /* "dont_toggle" state set? */
5982 if (g_object_get_data(G_OBJECT(summaryview->colorlabel_menu),
5983 "dont_toggle"))
5984 return;
5986 summary_set_colorlabel(summaryview, color, NULL);
5989 /* summary_set_colorlabel_color() - labelcolor parameter is the color *flag*
5990 * for the message; not the color index */
5991 void summary_set_colorlabel_color(GtkCMCTree *ctree, GtkCMCTreeNode *node,
5992 guint labelcolor)
5994 GdkColor color;
5995 GdkRGBA rgba;
5996 GtkStyle *style, *prev_style, *ctree_style;
5997 MsgInfo *msginfo;
5998 gint color_index;
6000 msginfo = gtk_cmctree_node_get_row_data(ctree, node);
6001 cm_return_if_fail(msginfo);
6003 color_index = labelcolor == 0 ? -1 : (gint)labelcolor - 1;
6004 ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
6005 prev_style = gtk_cmctree_node_get_row_style(ctree, node);
6007 if (color_index < 0 || color_index >= N_COLOR_LABELS) {
6008 if (!prev_style) return;
6009 style = gtk_style_copy(prev_style);
6010 color = ctree_style->text[GTK_STATE_NORMAL];
6011 style->text[GTK_STATE_NORMAL] = color;
6012 color = ctree_style->text[GTK_STATE_SELECTED];
6013 style->text[GTK_STATE_SELECTED] = color;
6014 } else {
6015 if (prev_style)
6016 style = gtk_style_copy(prev_style);
6017 else
6018 style = gtk_style_copy(ctree_style);
6019 rgba = colorlabel_get_color(color_index);
6020 GTKUT_GDKRGBA_TO_GDKCOLOR(rgba, color);
6021 style->text[GTK_STATE_NORMAL] = color;
6022 /* get the average of label color and selected fg color
6023 for visibility */
6024 style->text[GTK_STATE_SELECTED].red = (color.red + 3*ctree_style->text[GTK_STATE_SELECTED].red ) / 4;
6025 style->text[GTK_STATE_SELECTED].green = (color.green + 3*ctree_style->text[GTK_STATE_SELECTED].green) / 4;
6026 style->text[GTK_STATE_SELECTED].blue = (color.blue + 3*ctree_style->text[GTK_STATE_SELECTED].blue ) / 4;
6029 gtk_cmctree_node_set_row_style(ctree, node, style);
6030 g_object_unref(style);
6033 static void summary_set_row_colorlabel(SummaryView *summaryview, GtkCMCTreeNode *row, guint labelcolor)
6035 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6036 MsgInfo *msginfo;
6038 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6039 cm_return_if_fail(msginfo);
6041 summary_msginfo_change_flags(msginfo, MSG_COLORLABEL_TO_FLAGS(labelcolor), 0,
6042 MSG_CLABEL_FLAG_MASK, 0);
6043 summary_set_row_marks(summaryview, row);
6046 void summary_set_colorlabel(SummaryView *summaryview, guint labelcolor,
6047 GtkWidget *widget)
6049 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6050 GList *cur;
6051 gboolean froze = FALSE;
6053 if (prefs_common.ask_override_colorlabel) {
6054 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6055 gboolean ask = FALSE;
6056 AlertValue val;
6057 guint color;
6058 gboolean already_this_color_everywhere = TRUE;
6060 /* if clearing color labels (applying 'none', 0):
6061 - ask if at least one message has a non-0 color label set
6062 if applying a non-0 color label:
6063 - ask if at least one of the selected messages has a non-0 color label different
6064 from the one we want to apply.
6065 - don't ask if all messages have the same color label as the one we're applying
6067 for (cur = GTK_CMCLIST(ctree)->selection;
6068 !ask && cur != NULL && cur->data != NULL;
6069 cur = cur->next) {
6070 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(cur->data));
6071 if (msginfo) {
6072 color = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6073 if (labelcolor == 0) {
6074 /* clearing color labels */
6075 ask = (color != 0);
6076 } else {
6077 already_this_color_everywhere &= (color == labelcolor);
6078 ask = ((color != 0) && (color != labelcolor)) && !already_this_color_everywhere;
6083 if (ask) {
6084 gchar *msg;
6086 if (labelcolor == 0)
6087 msg = _("Do you really want to reset the color label of all selected messages?");
6088 else
6089 msg = _("Do you really want to apply this color label to all selected messages?");
6090 val = alertpanel_full(labelcolor == 0? _("Reset color label"): _("Set color label"), msg,
6091 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST,
6092 TRUE, NULL, ALERT_QUESTION);
6094 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
6095 return;
6096 else if (val & G_ALERTDISABLE)
6097 prefs_common.ask_override_colorlabel = FALSE;
6101 START_LONG_OPERATION(summaryview, FALSE);
6102 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
6103 summary_set_row_colorlabel(summaryview,
6104 GTK_CMCTREE_NODE(cur->data), labelcolor);
6105 END_LONG_OPERATION(summaryview);
6108 static gboolean summary_set_row_tag(SummaryView *summaryview, GtkCMCTreeNode *row, gboolean refresh, gboolean set, gint id)
6110 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6111 MsgInfo *msginfo;
6112 gchar *tags_str = NULL;
6113 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
6114 cm_return_val_if_fail(msginfo, FALSE);
6116 procmsg_msginfo_update_tags(msginfo, set, id);
6118 if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) {
6119 tags_str = procmsg_msginfo_get_tags_str(msginfo);
6120 gtk_cmctree_node_set_text(ctree, row,
6121 summaryview->col_pos[S_COL_TAGS],
6122 tags_str?tags_str:"-");
6123 g_free(tags_str);
6126 summary_set_row_marks(summaryview, row);
6127 if (row == summaryview->displayed) {
6128 return TRUE;
6130 return FALSE;
6133 void summary_set_tag(SummaryView *summaryview, gint tag_id,
6134 GtkWidget *widget)
6136 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
6137 GList *cur;
6138 gboolean set = tag_id > 0;
6139 gint real_id = set? tag_id:-tag_id;
6140 gboolean froze = FALSE;
6141 gboolean redisplay = FALSE;
6143 if (summary_is_locked(summaryview))
6144 return;
6145 START_LONG_OPERATION(summaryview, FALSE);
6146 folder_item_set_batch(summaryview->folder_item, TRUE);
6147 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) {
6148 redisplay |= summary_set_row_tag(summaryview,
6149 GTK_CMCTREE_NODE(cur->data), FALSE, set, real_id);
6151 folder_item_set_batch(summaryview->folder_item, FALSE);
6152 END_LONG_OPERATION(summaryview);
6153 if (redisplay)
6154 summary_redisplay_msg(summaryview);
6157 static void summary_tags_menu_item_activate_cb(GtkWidget *widget,
6158 gpointer data)
6160 gint id = GPOINTER_TO_INT(data);
6161 gboolean set = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
6162 SummaryView *summaryview;
6164 summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6165 cm_return_if_fail(summaryview != NULL);
6167 /* "dont_toggle" state set? */
6168 if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6169 "dont_toggle"))
6170 return;
6172 if (!set)
6173 id = -id;
6174 summary_set_tag(summaryview, id, NULL);
6177 static void summary_colorlabel_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6178 gpointer data)
6180 SummaryView *summaryview;
6181 GtkMenuShell *menu;
6182 GtkCheckMenuItem **items;
6183 gint n;
6184 GList *children, *cur, *sel;
6186 summaryview = (SummaryView *)data;
6187 cm_return_if_fail(summaryview != NULL);
6189 sel = GTK_CMCLIST(summaryview->ctree)->selection;
6190 if (!sel) return;
6192 menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
6194 cm_return_if_fail(menu != NULL);
6196 Xalloca(items, (N_COLOR_LABELS + 1) * sizeof(GtkWidget *), return);
6198 /* NOTE: don't return prematurely because we set the "dont_toggle"
6199 * state for check menu items */
6200 g_object_set_data(G_OBJECT(menu), "dont_toggle",
6201 GINT_TO_POINTER(1));
6203 /* clear items. get item pointers. */
6204 children = gtk_container_get_children(GTK_CONTAINER(menu));
6205 for (n = 0, cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6206 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6207 gtk_check_menu_item_set_active
6208 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6209 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(cur->data),
6210 TRUE);
6211 items[n] = GTK_CHECK_MENU_ITEM(cur->data);
6212 n++;
6216 g_list_free(children);
6218 if (n == (N_COLOR_LABELS + 1)) {
6219 /* iterate all messages and set the state of the appropriate
6220 * items */
6221 for (; sel != NULL; sel = sel->next) {
6222 MsgInfo *msginfo;
6223 gint clabel;
6225 msginfo = gtk_cmctree_node_get_row_data
6226 (GTK_CMCTREE(summaryview->ctree),
6227 GTK_CMCTREE_NODE(sel->data));
6228 if (msginfo) {
6229 clabel = MSG_GET_COLORLABEL_VALUE(msginfo->flags);
6230 if (!gtk_check_menu_item_get_active(items[clabel]))
6231 gtk_check_menu_item_set_active
6232 (items[clabel], TRUE);
6235 } else
6236 g_warning("invalid number of color elements (%d)", n);
6238 /* reset "dont_toggle" state */
6239 g_object_set_data(G_OBJECT(menu), "dont_toggle",
6240 GINT_TO_POINTER(0));
6243 static void summary_colorlabel_menu_create(SummaryView *summaryview, gboolean refresh)
6245 GtkWidget *label_menuitem;
6246 GtkWidget *menu;
6247 GtkWidget *item;
6248 gint i;
6249 gchar *accel_path = NULL;
6251 label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/ColorLabel");
6252 g_signal_connect(G_OBJECT(label_menuitem), "activate",
6253 G_CALLBACK(summary_colorlabel_menu_item_activate_item_cb),
6254 summaryview);
6255 gtk_widget_show(label_menuitem);
6257 menu = gtk_menu_new();
6259 gtk_menu_set_accel_group (GTK_MENU (menu),
6260 gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager));
6262 /* create sub items. for the menu item activation callback we pass the
6263 * index of label_colors[] as data parameter. for the None color we
6264 * pass an invalid (high) value. also we attach a data pointer so we
6265 * can always get back the SummaryView pointer. */
6267 item = gtk_check_menu_item_new_with_label(_("None"));
6268 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6269 g_signal_connect(G_OBJECT(item), "activate",
6270 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6271 GUINT_TO_POINTER(0));
6272 g_object_set_data(G_OBJECT(item), "summaryview", summaryview);
6273 gtk_widget_show(item);
6275 accel_path = g_strdup_printf("<ClawsColorLabels>/None");
6276 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6277 g_free(accel_path);
6278 gtk_accel_map_add_entry("<ClawsColorLabels>/None", GDK_KEY_0, GDK_CONTROL_MASK);
6280 /* create pixmap/label menu items */
6281 for (i = 0; i < N_COLOR_LABELS; i++) {
6282 item = colorlabel_create_check_color_menu_item(
6283 i, refresh, SUMMARY_COLORMENU);
6284 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6285 g_signal_connect(G_OBJECT(item), "activate",
6286 G_CALLBACK(summary_colorlabel_menu_item_activate_cb),
6287 GUINT_TO_POINTER(i + 1));
6288 g_object_set_data(G_OBJECT(item), "summaryview",
6289 summaryview);
6290 gtk_widget_show(item);
6291 accel_path = g_strdup_printf("<ClawsColorLabels>/%d", i+1);
6292 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6293 if (i < 9)
6294 gtk_accel_map_add_entry(accel_path, GDK_KEY_1+i, GDK_CONTROL_MASK);
6295 g_free(accel_path);
6296 g_signal_connect (gtk_ui_manager_get_accel_group(mainwindow_get_mainwindow()->ui_manager),
6297 "accel-changed", G_CALLBACK (mainwin_accel_changed_cb), item);
6300 gtk_widget_show(menu);
6301 gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6302 summaryview->colorlabel_menu = menu;
6305 static void summary_tags_menu_item_activate_item_cb(GtkMenuItem *menu_item,
6306 gpointer data)
6308 GtkMenuShell *menu;
6309 GList *children, *cur;
6310 GList *sel;
6311 GHashTable *menu_table = g_hash_table_new_full(
6312 g_direct_hash,
6313 g_direct_equal,
6314 NULL, NULL);
6315 GHashTable *menu_allsel_table = g_hash_table_new_full(
6316 g_direct_hash,
6317 g_direct_equal,
6318 NULL, NULL);
6319 gint sel_len;
6320 SummaryView *summaryview = (SummaryView *)data;
6321 cm_return_if_fail(summaryview != NULL);
6323 sel = GTK_CMCLIST(summaryview->ctree)->selection;
6324 if (!sel) return;
6326 menu = GTK_MENU_SHELL(summaryview->tags_menu);
6327 cm_return_if_fail(menu != NULL);
6329 /* NOTE: don't return prematurely because we set the "dont_toggle"
6330 * state for check menu items */
6331 g_object_set_data(G_OBJECT(menu), "dont_toggle",
6332 GINT_TO_POINTER(1));
6334 /* clear items. get item pointers. */
6335 children = gtk_container_get_children(GTK_CONTAINER(menu));
6336 for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6337 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6338 gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6339 "tag_id"));
6340 gtk_check_menu_item_set_active
6341 (GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6343 g_hash_table_insert(menu_table, GINT_TO_POINTER(id), GTK_CHECK_MENU_ITEM(cur->data));
6344 g_hash_table_insert(menu_allsel_table, GINT_TO_POINTER(id), GINT_TO_POINTER(0));
6348 g_list_free(children);
6350 /* iterate all messages and set the state of the appropriate
6351 * items */
6352 sel_len = 0;
6353 for (; sel != NULL; sel = sel->next) {
6354 MsgInfo *msginfo;
6355 GSList *tags = NULL;
6356 GtkCheckMenuItem *item;
6357 msginfo = gtk_cmctree_node_get_row_data
6358 (GTK_CMCTREE(summaryview->ctree),
6359 GTK_CMCTREE_NODE(sel->data));
6360 sel_len++;
6361 if (msginfo) {
6362 tags = msginfo->tags;
6363 if (!tags)
6364 continue;
6366 for (; tags; tags = tags->next) {
6367 gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, tags->data));
6368 item = g_hash_table_lookup(menu_table, GINT_TO_POINTER(tags->data));
6369 if (item && !gtk_check_menu_item_get_active(item)) {
6370 gtk_check_menu_item_set_active
6371 (item, TRUE);
6373 num_checked++;
6374 g_hash_table_replace(menu_allsel_table, tags->data, GINT_TO_POINTER(num_checked));
6379 children = gtk_container_get_children(GTK_CONTAINER(menu));
6380 for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
6381 if (GTK_IS_CHECK_MENU_ITEM(cur->data)) {
6382 gint id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cur->data),
6383 "tag_id"));
6384 gint num_checked = GPOINTER_TO_INT(g_hash_table_lookup(menu_allsel_table, GINT_TO_POINTER(id)));
6385 if (num_checked < sel_len && num_checked > 0)
6386 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), TRUE);
6387 else
6388 gtk_check_menu_item_set_inconsistent(GTK_CHECK_MENU_ITEM(cur->data), FALSE);
6391 g_list_free(children);
6392 g_hash_table_destroy(menu_table);
6393 g_hash_table_destroy(menu_allsel_table);
6394 /* reset "dont_toggle" state */
6395 g_object_set_data(G_OBJECT(menu), "dont_toggle",
6396 GINT_TO_POINTER(0));
6399 void summaryview_destroy(SummaryView *summaryview)
6401 if(summaryview->simplify_subject_preg) {
6402 regfree(summaryview->simplify_subject_preg);
6403 g_free(summaryview->simplify_subject_preg);
6404 summaryview->simplify_subject_preg = NULL;
6407 static void summary_tags_menu_item_apply_tags_activate_cb(GtkWidget *widget,
6408 gpointer data)
6410 SummaryView *summaryview;
6412 summaryview = g_object_get_data(G_OBJECT(widget), "summaryview");
6413 cm_return_if_fail(summaryview != NULL);
6415 /* "dont_toggle" state set? */
6416 if (g_object_get_data(G_OBJECT(summaryview->tags_menu),
6417 "dont_toggle"))
6418 return;
6420 tags_window_open(summary_get_selection(summaryview));
6423 static gint summary_tag_cmp_list(gconstpointer a, gconstpointer b)
6425 gint id_a = GPOINTER_TO_INT(a);
6426 gint id_b = GPOINTER_TO_INT(b);
6427 const gchar *tag_a = tags_get_tag(id_a);
6428 const gchar *tag_b = tags_get_tag(id_b);
6430 if (tag_a == NULL)
6431 return tag_b == NULL ? 0:1;
6433 if (tag_b == NULL)
6434 return 1;
6436 return g_utf8_collate(tag_a, tag_b);
6439 static void summary_tags_menu_create(SummaryView *summaryview, gboolean refresh)
6442 GtkWidget *label_menuitem;
6443 GtkWidget *menu;
6444 GtkWidget *item;
6445 GSList *cur = tags_get_list();
6446 GSList *orig = NULL;
6447 gboolean existing_tags = FALSE;
6448 gchar *accel_path = NULL;
6450 cur = orig = g_slist_sort(cur, summary_tag_cmp_list);
6451 label_menuitem = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, "/Menus/SummaryViewPopup/Tags");
6452 g_signal_connect(G_OBJECT(label_menuitem), "activate",
6453 G_CALLBACK(summary_tags_menu_item_activate_item_cb),
6454 summaryview);
6456 gtk_widget_show(label_menuitem);
6458 menu = gtk_menu_new();
6460 gtk_menu_set_accel_group (GTK_MENU (menu),
6461 gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager));
6463 /* create tags menu items */
6464 for (; cur; cur = cur->next) {
6465 gint id = GPOINTER_TO_INT(cur->data);
6466 const gchar *tag = tags_get_tag(id);
6467 item = gtk_check_menu_item_new_with_label(tag);
6468 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6469 g_signal_connect(G_OBJECT(item), "activate",
6470 G_CALLBACK(summary_tags_menu_item_activate_cb),
6471 GINT_TO_POINTER(id));
6472 g_object_set_data(G_OBJECT(item), "summaryview",
6473 summaryview);
6474 g_object_set_data(G_OBJECT(item), "tag_id",
6475 GINT_TO_POINTER(id));
6476 gtk_widget_show(item);
6477 accel_path = g_strconcat("<ClawsTags>/",tag, NULL);
6478 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6479 g_free(accel_path);
6480 existing_tags = TRUE;
6482 if (existing_tags) {
6483 /* separator */
6484 item = gtk_separator_menu_item_new();
6485 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6486 gtk_widget_show(item);
6489 item = gtk_menu_item_new_with_label(_("Modify tags..."));
6490 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6491 g_signal_connect(G_OBJECT(item), "activate",
6492 G_CALLBACK(summary_tags_menu_item_apply_tags_activate_cb),
6493 NULL);
6494 g_object_set_data(G_OBJECT(item), "summaryview",
6495 summaryview);
6496 gtk_widget_show(item);
6497 accel_path = g_strdup_printf("<ClawsTags>/ModifyTags");
6498 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
6499 g_free(accel_path);
6501 g_slist_free(orig);
6502 gtk_widget_show(menu);
6503 gtk_menu_item_set_submenu(GTK_MENU_ITEM(label_menuitem), menu);
6504 summaryview->tags_menu = menu;
6507 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data)
6509 SummaryView *summaryview = (SummaryView *)data;
6510 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6512 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL);
6514 return TRUE;
6517 #if !GENERIC_UMPC
6518 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column)
6520 MsgFlags flags;
6521 if (!info)
6522 return NULL;
6524 flags = info->flags;
6526 switch(summaryview->col_state[column].type) {
6527 case S_COL_STATUS:
6528 if (MSG_IS_IGNORE_THREAD(flags)) {
6529 return _("Ignored thread");
6530 } else if (MSG_IS_WATCH_THREAD(flags)) {
6531 return _("Watched thread");
6532 } else if (MSG_IS_SPAM(flags)) {
6533 return _("Spam");
6534 } else if (MSG_IS_NEW(flags)) {
6535 return _("New");
6536 } else if (MSG_IS_UNREAD(flags)) {
6537 return _("Unread");
6538 } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) {
6539 return _("Replied but also forwarded - click to see reply");
6540 } else if (MSG_IS_REPLIED(flags)) {
6541 return _("Replied - click to see reply");
6542 } else if (MSG_IS_FORWARDED(flags)) {
6543 return _("Forwarded");
6544 } else {
6545 return NULL;
6547 case S_COL_MARK:
6548 if (MSG_IS_DELETED(flags)) {
6549 return _("Deleted");
6550 } else if (MSG_IS_MARKED(flags)) {
6551 return _("Marked");
6552 } else if (MSG_IS_MOVE(flags)) {
6553 return _("To be moved");
6554 } else if (MSG_IS_COPY(flags)) {
6555 return _("To be copied");
6556 } else {
6557 return NULL;
6559 case S_COL_LOCKED:
6560 if (MSG_IS_LOCKED(flags)) {
6561 return _("Locked");
6562 } else {
6563 return NULL;
6565 case S_COL_MIME:
6566 if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) {
6567 return _("Signed, has attachment(s)");
6568 } else if (MSG_IS_SIGNED(flags)) {
6569 return _("Signed");
6570 } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) {
6571 return _("Encrypted, has attachment(s)");
6572 } else if (MSG_IS_ENCRYPTED(flags)) {
6573 return _("Encrypted");
6574 } else if (MSG_IS_WITH_ATTACHMENT(flags)) {
6575 return _("Has attachment(s)");
6576 } else {
6577 return NULL;
6579 default:
6580 return NULL;
6583 static gboolean tooltip_cb (GtkWidget *widget,
6584 gint x,
6585 gint y,
6586 gboolean keyboard_mode,
6587 GtkTooltip *tooltip,
6588 gpointer user_data)
6590 GtkCMCTree *ctree = GTK_CMCTREE(widget);
6591 SummaryView *summaryview = (SummaryView *)user_data;
6592 gint row = -1, column = -1;
6593 int offset = prefs_common.show_col_headers ? 24:0;
6594 GtkCMCTreeNode *node = NULL;
6595 gchar *text = NULL;
6596 gchar *formatted = NULL;
6597 MsgInfo *info = NULL;
6598 GdkRectangle rect;
6599 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6600 gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6601 if (!prefs_common.show_tooltips)
6602 return FALSE;
6604 if (y - offset < 0)
6605 return FALSE;
6607 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset,
6608 &row, &column))
6609 return FALSE;
6611 if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL)
6612 return FALSE;
6614 if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL)
6615 return FALSE;
6617 switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) {
6618 case GTK_CMCELL_TEXT:
6619 if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE)
6620 return FALSE;
6621 if (column == summaryview->col_pos[S_COL_FROM])
6622 text = info->from;
6623 else if (column == summaryview->col_pos[S_COL_TO])
6624 text = info->to;
6625 break;
6626 case GTK_CMCELL_PIXTEXT:
6627 if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text,
6628 NULL, NULL) != TRUE)
6629 return FALSE;
6630 break;
6631 default:
6632 if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL)
6633 return FALSE;
6636 if (!text || !*text)
6637 return FALSE;
6639 formatted = g_strdup(text);
6640 g_strstrip(formatted);
6642 if ((vert_layout || small_layout) && prefs_common.two_line_vert)
6643 gtk_tooltip_set_markup (tooltip, formatted);
6644 else
6645 gtk_tooltip_set_text (tooltip, formatted);
6646 g_free(formatted);
6648 rect.x = x - 2;
6649 rect.y = y - 2;
6650 rect.width = 12;
6651 rect.height= 12;
6652 gtk_tooltip_set_tip_area(tooltip, &rect);
6654 return TRUE;
6656 #endif
6658 static gboolean summary_header_button_pressed(GtkWidget *widget,
6659 GdkEvent *_event,
6660 gpointer user_data)
6662 GdkEventButton *event = (GdkEventButton *)_event;
6663 SummaryView *summaryview = (SummaryView *)user_data;
6665 cm_return_val_if_fail(summaryview != NULL, FALSE);
6667 /* Only handle single button presses. */
6668 if (event->type == GDK_2BUTTON_PRESS ||
6669 event->type == GDK_3BUTTON_PRESS)
6670 return FALSE;
6672 /* Handle right-click for context menu */
6673 if (event->button == 3) {
6674 /* Set up any menu items that need setting up. */
6675 summaryview->header_menu_lock = TRUE;
6676 cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager,
6677 "Menus/SummaryViewHeaderPopup/LockColumnHeaders",
6678 prefs_common_get_prefs()->summary_col_lock);
6679 summaryview->header_menu_lock = FALSE;
6681 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->headerpopupmenu), NULL);
6682 return TRUE;
6685 return FALSE;
6688 static GtkWidget *summary_ctree_create(SummaryView *summaryview)
6690 GtkWidget *ctree;
6691 gint *col_pos = summaryview->col_pos;
6692 SummaryColumnState *col_state;
6693 gchar *titles[N_SUMMARY_COLS];
6694 SummaryColumnType type;
6695 gint pos;
6696 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT);
6697 gboolean small_layout = (prefs_common.layout_mode == SMALL_LAYOUT);
6698 memset(titles, 0, sizeof(titles));
6700 col_state = prefs_summary_column_get_config();
6701 for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6702 summaryview->col_state[pos] = col_state[pos];
6703 type = col_state[pos].type;
6704 col_pos[type] = pos;
6705 titles[pos] = "dummy";
6707 col_state = summaryview->col_state;
6709 ctree = gtk_sctree_new_with_titles
6710 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles);
6712 gtk_widget_set_name(GTK_WIDGET(ctree), "summaryview_sctree");
6714 if (prefs_common.show_col_headers == FALSE)
6715 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
6717 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_MULTIPLE);
6718 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6719 GTK_JUSTIFY_CENTER);
6720 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6721 GTK_JUSTIFY_CENTER);
6722 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6723 GTK_JUSTIFY_CENTER);
6724 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6725 GTK_JUSTIFY_CENTER);
6726 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6727 GTK_JUSTIFY_RIGHT);
6728 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6729 GTK_JUSTIFY_RIGHT);
6730 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6731 GTK_JUSTIFY_RIGHT);
6732 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK],
6733 prefs_common.summary_col_size[S_COL_MARK]);
6734 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS],
6735 prefs_common.summary_col_size[S_COL_STATUS]);
6736 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED],
6737 prefs_common.summary_col_size[S_COL_LOCKED]);
6738 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME],
6739 prefs_common.summary_col_size[S_COL_MIME]);
6740 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT],
6741 prefs_common.summary_col_size[S_COL_SUBJECT]);
6742 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM],
6743 prefs_common.summary_col_size[S_COL_FROM]);
6744 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO],
6745 prefs_common.summary_col_size[S_COL_TO]);
6746 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE],
6747 prefs_common.summary_col_size[S_COL_DATE]);
6748 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE],
6749 prefs_common.summary_col_size[S_COL_SIZE]);
6750 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER],
6751 prefs_common.summary_col_size[S_COL_NUMBER]);
6752 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE],
6753 prefs_common.summary_col_size[S_COL_SCORE]);
6754 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS],
6755 prefs_common.summary_col_size[S_COL_TAGS]);
6757 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
6758 GTK_CMCTREE_EXPANDER_TRIANGLE);
6760 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
6762 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12);
6763 g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview);
6765 for (pos = 0; pos < N_SUMMARY_COLS; pos++) {
6766 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[pos].button,
6767 FALSE);
6768 if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6769 (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) ||
6770 pos == summaryview->col_pos[S_COL_DATE]) && (vert_layout || small_layout) &&
6771 prefs_common.two_line_vert)
6772 gtk_cmclist_set_column_visibility
6773 (GTK_CMCLIST(ctree), pos, FALSE);
6774 else
6775 gtk_cmclist_set_column_visibility
6776 (GTK_CMCLIST(ctree), pos, col_state[pos].visible);
6778 if (prefs_common.two_line_vert)
6779 gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert_layout||small_layout);
6781 /* connect signal to the buttons for sorting */
6782 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \
6783 g_signal_connect \
6784 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6785 "button-press-event", \
6786 G_CALLBACK(summary_header_button_pressed), \
6787 summaryview); \
6788 g_signal_connect \
6789 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \
6790 "clicked", \
6791 G_CALLBACK(func), \
6792 summaryview);
6794 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK , summary_mark_clicked)
6795 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked)
6796 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME , summary_mime_clicked)
6797 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked)
6798 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE , summary_size_clicked)
6799 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE , summary_date_clicked)
6800 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM , summary_from_clicked)
6801 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO , summary_to_clicked)
6802 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked)
6803 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE , summary_score_clicked)
6804 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED , summary_locked_clicked)
6805 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TAGS , summary_tags_clicked)
6807 #undef CLIST_BUTTON_SIGNAL_CONNECT
6809 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
6810 G_CALLBACK(summary_selected), summaryview);
6811 g_signal_connect(G_OBJECT(ctree), "tree_unselect_row",
6812 G_CALLBACK(summary_unselected), summaryview);
6813 g_signal_connect(G_OBJECT(ctree), "button_press_event",
6814 G_CALLBACK(summary_button_pressed),
6815 summaryview);
6816 g_signal_connect(G_OBJECT(ctree), "popup-menu",
6817 G_CALLBACK(summary_popup_menu), summaryview);
6818 g_signal_connect(G_OBJECT(ctree), "button_release_event",
6819 G_CALLBACK(summary_button_released),
6820 summaryview);
6821 g_signal_connect(G_OBJECT(ctree), "key_press_event",
6822 G_CALLBACK(summary_key_pressed), summaryview);
6823 g_signal_connect(G_OBJECT(ctree), "resize_column",
6824 G_CALLBACK(summary_col_resized), summaryview);
6825 g_signal_connect(G_OBJECT(ctree), "open_row",
6826 G_CALLBACK(summary_open_row), summaryview);
6828 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
6829 G_CALLBACK(summary_tree_expanded),
6830 summaryview);
6831 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
6832 G_CALLBACK(summary_tree_collapsed),
6833 summaryview);
6835 g_signal_connect(G_OBJECT(ctree), "start_drag",
6836 G_CALLBACK(summary_start_drag),
6837 summaryview);
6838 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
6839 G_CALLBACK(summary_drag_data_get),
6840 summaryview);
6841 g_signal_connect(G_OBJECT(ctree), "drag_end",
6842 G_CALLBACK(summary_drag_end),
6843 summaryview);
6845 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
6846 summary_drag_types, 3,
6847 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
6849 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
6850 G_CALLBACK(summary_drag_data_received),
6851 summaryview);
6853 g_signal_connect(G_OBJECT(ctree), "drag_motion",
6854 G_CALLBACK(summary_drag_motion_cb),
6855 summaryview);
6857 #if !GENERIC_UMPC
6858 g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL);
6859 g_signal_connect(G_OBJECT(ctree), "query-tooltip",
6860 G_CALLBACK(tooltip_cb),
6861 summaryview);
6862 #endif
6863 return ctree;
6866 void summary_set_column_order(SummaryView *summaryview)
6868 GtkWidget *ctree;
6869 GtkWidget *scrolledwin = summaryview->scrolledwin;
6870 FolderItem *item;
6871 guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected);
6872 guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed);
6874 item = summaryview->folder_item;
6876 summary_clear_all(summaryview);
6877 gtk_widget_destroy(summaryview->ctree);
6879 summaryview->ctree = ctree = summary_ctree_create(summaryview);
6880 summary_set_fonts(summaryview);
6881 summary_set_column_titles(summaryview);
6882 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6883 GTK_CMCLIST(ctree)->hadjustment);
6884 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
6885 GTK_CMCLIST(ctree)->vadjustment);
6886 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
6887 gtk_widget_show(ctree);
6889 summary_show(summaryview, item, FALSE);
6891 summary_select_by_msgnum(summaryview, selected_msgnum, FALSE);
6893 summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum);
6894 summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum);
6895 if (!summaryview->displayed)
6896 messageview_clear(summaryview->messageview);
6897 else
6898 summary_redisplay_msg(summaryview);
6900 /* get normal row height */
6901 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
6902 normal_row_height = GTK_CMCLIST(ctree)->row_height;
6904 if ((prefs_common.layout_mode == SMALL_LAYOUT || prefs_common.layout_mode == VERTICAL_LAYOUT) &&
6905 prefs_common.two_line_vert) {
6906 gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2);
6911 /* callback functions */
6913 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event,
6914 SummaryView *summaryview)
6916 if (event) {
6917 folderview_grab_focus(summaryview->folderview);
6918 mainwindow_exit_folder(summaryview->mainwin);
6920 return TRUE;
6923 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event,
6924 SummaryView *summaryview)
6926 if (event)
6927 summary_toggle_view(summaryview);
6928 return TRUE;
6930 #ifdef GENERIC_UMPC
6931 static void summary_toggle_multiple_pressed(GtkWidget *widget,
6932 SummaryView *summaryview)
6934 GTK_SCTREE(summaryview->ctree)->force_additive_sel =
6935 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
6937 #endif
6938 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event,
6939 SummaryView *summaryview)
6941 if (!event) return FALSE;
6942 if (event->window != GTK_CMCLIST(ctree)->clist_window) return FALSE;
6944 if (event->button == 3) {
6945 /* right clicked */
6946 summary_set_menu_sensitive(summaryview);
6947 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL);
6948 } else if (event->button == 2) {
6949 summaryview->display_msg = messageview_is_visible(summaryview->messageview);
6950 } else if (event->button == 1) {
6951 if (!prefs_common.emulate_emacs &&
6952 messageview_is_visible(summaryview->messageview))
6953 summaryview->display_msg = TRUE;
6956 return FALSE;
6959 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event,
6960 SummaryView *summaryview)
6962 return FALSE;
6965 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event)
6967 if (!summaryview)
6968 return FALSE;
6969 if (summary_is_list(summaryview))
6970 return summary_key_pressed(summaryview->ctree, event, summaryview);
6971 else
6972 return FALSE;
6975 #define BREAK_ON_MODIFIER_KEY() \
6976 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
6978 /* Copied from gtkcmclist.c, if it changes there, it has to change
6979 * here as well. This is an ugly hack, there must be a better way to
6980 * find out how much to move for page up/down. */
6981 #define CELL_SPACING 1
6982 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event,
6983 SummaryView *summaryview)
6985 GtkCMCTree *ctree = GTK_CMCTREE(widget);
6986 GtkCMCTreeNode *node;
6987 MessageView *messageview;
6988 GtkAdjustment *adj;
6989 gboolean mod_pressed;
6991 if (!event)
6992 return TRUE;
6994 if (quicksearch_has_focus(summaryview->quicksearch))
6995 return FALSE;
6997 messageview = summaryview->messageview;
6999 mod_pressed =
7000 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
7002 if (summaryview->selected) {
7003 gboolean handled = FALSE;
7004 switch (event->keyval) {
7005 case GDK_KEY_space: /* Page down or go to the next */
7006 handled = TRUE;
7007 if (event->state & GDK_CONTROL_MASK) {
7008 handled = FALSE;
7009 break;
7011 if (event->state & GDK_SHIFT_MASK)
7012 mimeview_scroll_page(messageview->mimeview, TRUE);
7013 if (summaryview->displayed != summaryview->selected) {
7014 summary_display_msg(summaryview,
7015 summaryview->selected);
7016 } else {
7017 if (mod_pressed) {
7018 if (!mimeview_scroll_page(messageview->mimeview, TRUE))
7019 summary_select_prev_unread(summaryview);
7020 } else {
7021 if (!mimeview_scroll_page(messageview->mimeview, FALSE))
7022 summary_select_next_unread(summaryview);
7025 break;
7026 case GDK_KEY_BackSpace: /* Page up */
7027 handled = TRUE;
7028 mimeview_scroll_page(messageview->mimeview, TRUE);
7029 break;
7030 case GDK_KEY_Return: /* Scroll up/down one line */
7031 case GDK_KEY_KP_Enter:
7032 handled = TRUE;
7033 if (summaryview->displayed != summaryview->selected) {
7034 #ifndef GENERIC_UMPC
7035 summary_display_msg(summaryview,
7036 summaryview->selected);
7037 #else
7038 summary_open_row(NULL, summaryview);
7039 #endif
7040 break;
7042 mimeview_scroll_one_line(messageview->mimeview, mod_pressed);
7043 break;
7046 if (handled)
7047 return FALSE;
7049 if (summary_is_locked(summaryview))
7050 return TRUE;
7052 switch (event->keyval) {
7053 case GDK_KEY_Left: /* Move focus */
7054 adj = gtk_scrolled_window_get_hadjustment
7055 (GTK_SCROLLED_WINDOW(summaryview->scrolledwin));
7056 if (gtk_adjustment_get_lower(adj) != gtk_adjustment_get_value(adj))
7057 break;
7058 /* FALLTHROUGH */
7059 case GDK_KEY_Escape:
7060 folderview_grab_focus(summaryview->folderview);
7061 mainwindow_exit_folder(summaryview->mainwin);
7062 return TRUE;
7063 case GDK_KEY_Home: case GDK_KEY_KP_Home:
7064 case GDK_KEY_End: case GDK_KEY_KP_End:
7065 case GDK_KEY_Up: case GDK_KEY_KP_Up:
7066 case GDK_KEY_Down: case GDK_KEY_KP_Down:
7067 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
7068 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
7069 if ((node = summaryview->selected) != NULL) {
7070 GtkCMCTreeNode *next = NULL;
7071 switch (event->keyval) {
7072 case GDK_KEY_Home: case GDK_KEY_KP_Home:
7073 next = gtk_cmctree_node_nth(ctree, 0);
7074 break;
7075 case GDK_KEY_End: case GDK_KEY_KP_End:
7076 next = gtk_cmctree_node_nth(ctree,
7077 g_list_length(GTK_CMCLIST(ctree)->row_list)-1);
7078 break;
7079 case GDK_KEY_Up: case GDK_KEY_KP_Up:
7080 next = gtk_cmctree_node_nth(ctree,
7081 MAX(0, GTK_CMCLIST(ctree)->focus_row - 1));
7082 break;
7083 case GDK_KEY_Down: case GDK_KEY_KP_Down:
7084 next = gtk_cmctree_node_nth(ctree,
7085 MIN(GTK_CMCLIST(ctree)->focus_row + 1, GTK_CMCLIST(ctree)->rows - 1));
7086 break;
7087 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up:
7088 next = gtk_cmctree_node_nth(ctree,
7089 MAX(0, GTK_CMCLIST(ctree)->focus_row -
7090 (2 * GTK_CMCLIST(ctree)->clist_window_height -
7091 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
7092 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
7093 break;
7094 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down:
7095 next = gtk_cmctree_node_nth(ctree,
7096 MIN(GTK_CMCLIST(ctree)->rows - 1, GTK_CMCLIST(ctree)->focus_row +
7097 (2 * GTK_CMCLIST(ctree)->clist_window_height -
7098 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) /
7099 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING))));
7100 break;
7103 if (next != NULL &&
7104 next != gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row)) {
7105 gtk_sctree_select_with_state
7106 (GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) );
7108 /* Deprecated - what are the non-deprecated equivalents? */
7109 if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL)
7110 gtkut_ctree_node_move_if_on_the_edge(GTK_CMCTREE(ctree), next, -1);
7111 if (!mod_pressed)
7112 summary_select_node(summaryview, summaryview->selected, OPEN_SELECTED_ON_DIRECTIONAL);
7113 summaryview->selected = next;
7116 return TRUE;
7117 default:
7118 break;
7121 if (!summaryview->selected) {
7122 node = gtk_cmctree_node_nth(ctree, 0);
7123 if (node)
7124 gtk_sctree_select(GTK_SCTREE(ctree), node);
7125 else
7126 return TRUE;
7129 switch (event->keyval) {
7130 case GDK_KEY_Delete:
7131 BREAK_ON_MODIFIER_KEY();
7132 summary_delete_trash(summaryview);
7133 break;
7134 default:
7135 break;
7137 return FALSE;
7139 #undef CELL_SPACING
7141 static void quicksearch_execute_cb(QuickSearch *quicksearch, gpointer data)
7143 SummaryView *summaryview = data;
7145 summaryview_reset_recursive_folder_match(summaryview);
7146 if (summary_show(summaryview, summaryview->folder_item, FALSE))
7147 summaryview_quicksearch_recurse(summaryview);
7148 else
7149 summaryview_reset_recursive_folder_match(summaryview);
7152 static void tog_searchbar_cb(GtkWidget *w, gpointer data)
7154 SummaryView *summaryview = (SummaryView *)data;
7156 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
7157 prefs_common.show_searchbar = TRUE;
7158 quicksearch_show(summaryview->quicksearch);
7159 } else {
7160 prefs_common.show_searchbar = FALSE;
7161 quicksearch_hide(summaryview->quicksearch);
7165 void summaryview_activate_quicksearch(SummaryView *summaryview, gboolean show)
7167 prefs_common.show_searchbar = show;
7168 gtk_toggle_button_set_active(
7169 GTK_TOGGLE_BUTTON(summaryview->toggle_search),
7170 show);
7171 if (show) {
7172 quicksearch_show(summaryview->quicksearch);
7173 } else {
7174 quicksearch_hide(summaryview->quicksearch);
7175 summary_grab_focus(summaryview);
7179 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview)
7181 if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item))
7182 summary_reedit(summaryview);
7183 else
7184 summary_open_msg(summaryview);
7186 summaryview->display_msg = FALSE;
7189 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7190 SummaryView *summaryview)
7192 summary_set_row_marks(summaryview, node);
7193 if (prefs_common.bold_unread) {
7194 while (node) {
7195 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7196 if (GTK_CMCTREE_ROW(node)->children)
7197 summary_set_row_marks(summaryview, node);
7198 node = next;
7203 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
7204 SummaryView *summaryview)
7206 gtk_sctree_select(GTK_SCTREE(ctree), node);
7207 summary_set_row_marks(summaryview, node);
7210 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7211 gint column, SummaryView *summaryview)
7213 if (summary_is_locked(summaryview)
7214 || GTK_SCTREE(ctree)->selecting_range) {
7215 return;
7218 summary_status_show(summaryview);
7221 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
7222 gint column, SummaryView *summaryview)
7224 const GList *list, *cur;
7225 MessageView *msgview;
7226 MsgInfo *msginfo;
7227 gboolean marked_unread = FALSE;
7229 if (summary_is_locked(summaryview)
7230 && !GTK_SCTREE(ctree)->selecting_range
7231 && summaryview->messageview
7232 && summaryview->messageview->mimeview
7233 && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT
7234 && summaryview->messageview->mimeview->textview->loading) {
7235 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
7236 summaryview->messageview->mimeview->textview->stop_loading = TRUE;
7238 data->ctree = ctree;
7239 data->row = row;
7240 data->node = NULL;
7241 data->column = column;
7242 data->summaryview = summaryview;
7243 debug_print("postponing open of message till end of load\n");
7244 g_timeout_add(100, summary_select_retry, data);
7245 return;
7247 if (summary_is_locked(summaryview)
7248 || GTK_SCTREE(ctree)->selecting_range) {
7249 return;
7252 summary_status_show(summaryview);
7254 if (GTK_CMCLIST(ctree)->selection &&
7255 GTK_CMCLIST(ctree)->selection->next) {
7256 summaryview->display_msg = FALSE;
7257 summary_set_menu_sensitive(summaryview);
7258 toolbar_main_set_sensitive(summaryview->mainwin);
7259 return;
7262 summaryview->selected = row;
7264 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7265 cm_return_if_fail(msginfo != NULL);
7267 main_create_mailing_list_menu (summaryview->mainwin, msginfo);
7268 toolbar_set_learn_button
7269 (summaryview->mainwin->toolbar,
7270 MSG_IS_SPAM(msginfo->flags)?LEARN_HAM:LEARN_SPAM);
7272 switch (column < 0 ? column : summaryview->col_state[column].type) {
7273 case S_COL_MARK:
7274 if (!MSG_IS_DELETED(msginfo->flags) &&
7275 !MSG_IS_MOVE(msginfo->flags) &&
7276 !MSG_IS_COPY(msginfo->flags)) {
7277 if (MSG_IS_MARKED(msginfo->flags)) {
7278 summary_unmark_row(summaryview, row);
7279 summary_status_show(summaryview);
7280 } else {
7281 summary_mark_row(summaryview, row);
7282 summary_status_show(summaryview);
7285 break;
7286 case S_COL_STATUS:
7287 if (MSG_IS_UNREAD(msginfo->flags)) {
7288 summary_mark_row_as_read(summaryview, row);
7289 summary_status_show(summaryview);
7290 } else if (MSG_IS_SPAM(msginfo->flags)) {
7291 if (procmsg_spam_learner_learn(msginfo, NULL, FALSE) == 0)
7292 summary_msginfo_unset_flags(msginfo, MSG_SPAM, 0);
7293 else
7294 log_error(LOG_PROTOCOL, _("An error happened while learning.\n"));
7295 } else if (!MSG_IS_REPLIED(msginfo->flags) &&
7296 !MSG_IS_FORWARDED(msginfo->flags)) {
7297 marked_unread = TRUE;
7298 } else if (MSG_IS_REPLIED(msginfo->flags)) {
7299 summary_find_answers(summaryview, msginfo);
7300 return;
7302 break;
7303 case S_COL_LOCKED:
7304 if (MSG_IS_LOCKED(msginfo->flags)) {
7305 summary_unlock_row(summaryview, row);
7306 summary_status_show(summaryview);
7308 else {
7309 summary_lock_row(summaryview, row);
7310 summary_status_show(summaryview);
7312 break;
7313 default:
7314 break;
7317 list = messageview_get_msgview_list();
7318 for (cur = list; cur != NULL; cur = cur->next) {
7319 msgview = (MessageView *) cur->data;
7321 if (msgview->new_window && msgview->update_needed) {
7322 MsgInfo *new_msginfo = summary_get_selected_msg(summaryview);
7323 messageview_show(msgview, new_msginfo, msgview->all_headers);
7324 msgview->update_needed = FALSE;
7328 if (summaryview->display_msg) {
7329 summaryview->display_msg = FALSE;
7330 if (summaryview->displayed != row) {
7331 summary_display_msg(summaryview, row);
7332 if (marked_unread) {
7333 summary_mark_row_as_unread(summaryview, row);
7334 summary_status_show(summaryview);
7336 return;
7340 if (marked_unread) {
7341 summary_mark_row_as_unread(summaryview, row);
7342 summary_status_show(summaryview);
7345 summary_set_menu_sensitive(summaryview);
7346 toolbar_main_set_sensitive(summaryview->mainwin);
7349 static void summary_col_resized(GtkCMCList *clist, gint column, gint width,
7350 SummaryView *summaryview)
7352 SummaryColumnType type = summaryview->col_state[column].type;
7354 prefs_common.summary_col_size[type] = width;
7359 * \brief get List of msginfo selected in SummaryView
7361 * \param summaryview
7363 * \return GSList holding MsgInfo
7365 GSList *summary_get_selection(SummaryView *summaryview)
7367 GList *sel = NULL;
7368 GSList *msginfo_list = NULL;
7370 cm_return_val_if_fail(summaryview != NULL, NULL);
7372 sel = GTK_CMCLIST(summaryview->ctree)->selection;
7374 cm_return_val_if_fail(sel != NULL, NULL);
7376 for ( ; sel != NULL; sel = sel->next)
7377 msginfo_list =
7378 g_slist_prepend(msginfo_list,
7379 gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree),
7380 GTK_CMCTREE_NODE(sel->data)));
7381 return g_slist_reverse(msginfo_list);
7385 * \brief get number of messages currently selected in SummaryView
7387 * \param summaryview
7389 * \return number of messages currently selected
7391 guint summary_get_selection_count(SummaryView *summaryview)
7393 cm_return_val_if_fail(summaryview != NULL, 0);
7394 return g_list_length(GTK_CMCLIST(summaryview->ctree)->selection);
7397 static void summary_sort_by_column_click(SummaryView *summaryview,
7398 FolderSortKey sort_key)
7400 if (prefs_common.summary_col_lock) {
7401 debug_print("summaryview columns locked, ignoring\n");
7402 return;
7405 GtkCMCTreeNode *node = NULL;
7406 START_TIMING("");
7407 if (summaryview->sort_key == sort_key)
7408 summary_sort(summaryview, sort_key,
7409 summaryview->sort_type == SORT_ASCENDING
7410 ? SORT_DESCENDING : SORT_ASCENDING);
7411 else
7412 summary_sort(summaryview, sort_key, summaryview->sort_type);
7414 node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list);
7416 summary_freeze(summaryview);
7417 if (prefs_common.bold_unread) {
7418 while (node) {
7419 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node);
7420 if (GTK_CMCTREE_ROW(node)->children)
7421 summary_set_row_marks(summaryview, node);
7422 node = next;
7425 summary_thaw(summaryview);
7426 END_TIMING();
7429 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview)
7431 summary_sort_by_column_click(summaryview, SORT_BY_MARK);
7434 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview)
7436 summary_sort_by_column_click(summaryview, SORT_BY_STATUS);
7439 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview)
7441 summary_sort_by_column_click(summaryview, SORT_BY_MIME);
7444 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview)
7446 summary_sort_by_column_click(summaryview, SORT_BY_NUMBER);
7449 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview)
7451 summary_sort_by_column_click(summaryview, SORT_BY_SIZE);
7454 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview)
7456 if (summaryview->sort_key == SORT_BY_THREAD_DATE)
7457 summary_sort_by_column_click(summaryview, SORT_BY_THREAD_DATE);
7458 else
7459 summary_sort_by_column_click(summaryview, SORT_BY_DATE);
7462 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview)
7464 if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible)
7465 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7466 else
7467 summary_sort_by_column_click(summaryview, SORT_BY_TO);
7470 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview)
7472 if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible)
7473 summary_sort_by_column_click(summaryview, SORT_BY_TO);
7474 else
7475 summary_sort_by_column_click(summaryview, SORT_BY_FROM);
7478 static void summary_subject_clicked(GtkWidget *button,
7479 SummaryView *summaryview)
7481 summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT);
7484 static void summary_score_clicked(GtkWidget *button,
7485 SummaryView *summaryview)
7487 summary_sort_by_column_click(summaryview, SORT_BY_SCORE);
7490 static void summary_locked_clicked(GtkWidget *button,
7491 SummaryView *summaryview)
7493 summary_sort_by_column_click(summaryview, SORT_BY_LOCKED);
7496 static void summary_tags_clicked(GtkWidget *button,
7497 SummaryView *summaryview)
7499 summary_sort_by_column_click(summaryview, SORT_BY_TAGS);
7502 static void summary_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
7503 SummaryView *summaryview)
7505 GdkDragContext *context;
7507 cm_return_if_fail(summaryview != NULL);
7508 cm_return_if_fail(summaryview->folder_item != NULL);
7509 cm_return_if_fail(summaryview->folder_item->folder != NULL);
7511 if (summaryview->selected == NULL) return;
7513 context = gtk_drag_begin_with_coordinates(widget, summaryview->target_list,
7514 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event,
7515 -1, -1);
7516 gtk_drag_set_icon_default(context);
7517 if (prefs_common.layout_mode == SMALL_LAYOUT) {
7518 GtkWidget *paned = gtk_widget_get_parent(GTK_WIDGET_PTR(summaryview));
7519 if (paned && GTK_IS_PANED(paned)) {
7520 mainwindow_reset_paned(GTK_PANED(paned));
7525 static gboolean summary_return_to_list(void *data)
7527 SummaryView *summaryview = (SummaryView *)data;
7528 mainwindow_enter_folder(summaryview->mainwin);
7529 return FALSE;
7532 static void summary_drag_end (GtkWidget *widget,
7533 GdkDragContext *drag_context,
7534 SummaryView *summaryview)
7536 if (prefs_common.layout_mode == SMALL_LAYOUT) {
7537 g_timeout_add(250, summary_return_to_list, summaryview);
7541 static void summary_drag_data_get(GtkWidget *widget,
7542 GdkDragContext *drag_context,
7543 GtkSelectionData *selection_data,
7544 guint info,
7545 guint time,
7546 SummaryView *summaryview)
7548 if (info == TARGET_MAIL_URI_LIST) {
7549 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7550 GList *cur;
7551 MsgInfo *msginfo;
7552 gchar *mail_list = NULL, *tmp1, *tmp2;
7554 for (cur = GTK_CMCLIST(ctree)->selection;
7555 cur != NULL && cur->data != NULL; cur = cur->next) {
7556 msginfo = gtk_cmctree_node_get_row_data
7557 (ctree, GTK_CMCTREE_NODE(cur->data));
7558 tmp2 = procmsg_get_message_file(msginfo);
7559 if (!tmp2) continue;
7560 if (msginfo->subject) {
7561 gchar *san_subject = g_strdup(msginfo->subject);
7562 gchar *dest = NULL;
7563 subst_for_filename(san_subject);
7564 dest = g_strdup_printf("%s%s%s.%d.txt",
7565 get_tmp_dir(),
7566 G_DIR_SEPARATOR_S,
7567 san_subject, msginfo->msgnum);
7568 g_free(san_subject);
7569 san_subject = g_filename_from_utf8(dest, -1, NULL, NULL, NULL);
7570 if (!san_subject) {
7571 g_warning("failed to convert encoding of file name: %s", dest);
7572 } else {
7573 g_free(dest);
7574 dest = san_subject;
7576 if (copy_file(tmp2, dest, TRUE) < 0) {
7577 g_warning("summary_drag_data_get: can't copy %s to %s",
7578 tmp2, dest);
7580 g_free(tmp2);
7581 tmp2 = dest;
7583 tmp1 = g_filename_to_uri(tmp2, NULL, NULL);
7584 g_free(tmp2);
7585 tmp2 = g_strconcat(tmp1, "\r\n", NULL);
7586 g_free(tmp1);
7587 tmp1 = tmp2;
7589 if (!mail_list) {
7590 mail_list = tmp1;
7591 } else {
7592 tmp2 = g_strconcat(mail_list, tmp1, NULL);
7593 g_free(mail_list);
7594 g_free(tmp1);
7595 mail_list = tmp2;
7599 if (mail_list != NULL) {
7600 gtk_selection_data_set(selection_data,
7601 gtk_selection_data_get_target(selection_data), 8,
7602 mail_list, strlen(mail_list));
7603 g_free(mail_list);
7605 } else if (info == TARGET_DUMMY) {
7606 if (GTK_CMCLIST(summaryview->ctree)->selection)
7607 gtk_selection_data_set(selection_data,
7608 gtk_selection_data_get_target(selection_data), 8,
7609 "Dummy-Summaryview",
7610 strlen("Dummy-Summaryview")+1);
7611 } else if (info == TARGET_MAIL_CM_PATH_LIST) {
7612 /* content: folder_item_identifier\nmsgid1\nmsgid2\nmsgid3 */
7614 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7615 GList *cur;
7616 MsgInfo *msginfo;
7617 gchar *path_list = NULL;
7619 /* identifier */
7620 if(GTK_CMCLIST(ctree)->selection != NULL) {
7621 msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->selection->data));
7622 if(msginfo && msginfo->folder)
7623 path_list = folder_item_get_identifier(msginfo->folder);
7626 if (path_list != NULL) {
7627 for (cur = GTK_CMCLIST(ctree)->selection;
7628 cur != NULL && cur->data != NULL && path_list != NULL; cur = cur->next) {
7629 gchar *tmp;
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);
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);
7645 } else {
7646 g_warning("summary_drag_data_get: no folder item identifier");
7651 static gboolean summary_drag_motion_cb(GtkWidget *widget,
7652 GdkDragContext *context,
7653 gint x,
7654 gint y,
7655 guint time,
7656 SummaryView *summaryview)
7658 FolderItem *item = summaryview->folder_item;
7659 if (!(item && item->folder && folder_item_parent(item) != NULL
7660 && FOLDER_CLASS(item->folder)->add_msg != NULL)) {
7661 gdk_drag_status(context, 0, time);
7662 return FALSE;
7663 } else if (gtk_drag_get_source_widget(context) ==
7664 mainwindow_get_mainwindow()->folderview->ctree) {
7665 /* no folders */
7666 gdk_drag_status(context, 0, time);
7667 return FALSE;
7668 } else if (gtk_drag_get_source_widget(context) ==
7669 summaryview->ctree) {
7670 /* not from same folder */
7671 gdk_drag_status(context, 0, time);
7672 return FALSE;
7673 } else {
7674 gdk_drag_status(context, GDK_ACTION_COPY, time);
7675 return TRUE;
7679 static void summary_drag_data_received(GtkWidget *widget,
7680 GdkDragContext *drag_context,
7681 gint x,
7682 gint y,
7683 GtkSelectionData *data,
7684 guint info,
7685 guint time,
7686 SummaryView *summaryview)
7688 if (info == TARGET_MAIL_URI_LIST) {
7689 FolderItem *item = summaryview->folder_item;
7690 if (!item) {
7691 gtk_drag_finish(drag_context, FALSE, FALSE, time);
7692 return;
7693 } else {
7694 folderview_finish_dnd(gtk_selection_data_get_data(data),
7695 drag_context, time, item);
7701 /* custom compare functions for sorting */
7703 static gint summary_cmp_by_date(GtkCMCList *clist,
7704 gconstpointer ptr1, gconstpointer ptr2)
7706 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7707 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7708 gint res;
7709 if (!msginfo1 || !msginfo2)
7710 return -1;
7712 res = (msginfo1->date_t - msginfo2->date_t);
7713 if (res == 0)
7714 res = msginfo1->msgnum - msginfo2->msgnum;
7715 return res;
7718 #define CMP_FUNC_DEF(func_name, val) \
7719 static gint func_name(GtkCMCList *clist, \
7720 gconstpointer ptr1, gconstpointer ptr2) \
7722 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; \
7723 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; \
7724 gint res; \
7725 if (!msginfo1 || !msginfo2) \
7726 return -1; \
7728 res = (val); \
7729 return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2); \
7732 CMP_FUNC_DEF(summary_cmp_by_mark,
7733 MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags))
7734 CMP_FUNC_DEF(summary_cmp_by_status,
7735 (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2))
7736 - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) )
7737 CMP_FUNC_DEF(summary_cmp_by_mime,
7738 MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags))
7739 CMP_FUNC_DEF(summary_cmp_by_label,
7740 MSG_GET_COLORLABEL(msginfo1->flags) -
7741 MSG_GET_COLORLABEL(msginfo2->flags))
7742 CMP_FUNC_DEF(summary_cmp_by_locked,
7743 MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags))
7745 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum)
7746 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size)
7748 #undef CMP_FUNC_DEF
7750 static gint summary_cmp_by_subject(GtkCMCList *clist,
7751 gconstpointer ptr1,
7752 gconstpointer ptr2)
7754 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7755 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7756 gint res;
7758 if (!msginfo1->subject)
7759 return (msginfo2->subject != NULL);
7760 if (!msginfo2->subject)
7761 return -1;
7763 res = subject_compare_for_sort
7764 (msginfo1->subject, msginfo2->subject);
7765 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7768 static gint summary_cmp_by_thread_date(GtkCMCList *clist,
7769 gconstpointer ptr1,
7770 gconstpointer ptr2)
7772 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7773 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7774 gint thread_diff = msginfo1->thread_date - msginfo2->thread_date;
7776 if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0)
7777 return thread_diff;
7778 else
7779 return msginfo1->date_t - msginfo2->date_t;
7782 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1,
7783 gconstpointer ptr2)
7785 const gchar *str1, *str2;
7786 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7787 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7788 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7789 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7790 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7791 gint res;
7793 cm_return_val_if_fail(sv, -1);
7794 if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) {
7795 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text;
7796 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text;
7797 } else {
7798 str1 = msginfo1->from;
7799 str2 = msginfo2->from;
7802 if (!str1)
7803 return str2 != NULL;
7805 if (!str2)
7806 return -1;
7808 res = g_utf8_collate(str1, str2);
7809 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7812 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1,
7813 gconstpointer ptr2)
7815 const gchar *str1, *str2;
7816 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7817 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7818 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7819 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7820 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7821 gint res;
7822 cm_return_val_if_fail(sv, -1);
7824 if (sv->col_state[sv->col_pos[S_COL_TO]].visible) {
7825 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text;
7826 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text;
7827 } else {
7828 str1 = msginfo1->to;
7829 str2 = msginfo2->to;
7832 if (!str1)
7833 return str2 != NULL;
7835 if (!str2)
7836 return -1;
7838 res = g_utf8_collate(str1, str2);
7839 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7842 static gint summary_cmp_by_tags(GtkCMCList *clist, gconstpointer ptr1,
7843 gconstpointer ptr2)
7845 gchar *str1, *str2;
7846 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7847 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7848 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7849 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7850 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7851 gint res;
7852 cm_return_val_if_fail(sv, -1);
7854 if (sv->col_state[sv->col_pos[S_COL_TAGS]].visible) {
7855 str1 = g_strdup(GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TAGS]])->text);
7856 str2 = g_strdup(GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TAGS]])->text);
7857 } else {
7858 str1 = procmsg_msginfo_get_tags_str(msginfo1);
7859 str2 = procmsg_msginfo_get_tags_str(msginfo2);
7862 if (!str1) {
7863 res = (str2 != NULL);
7864 g_free(str2);
7865 return res;
7867 if (!str2) {
7868 g_free(str1);
7869 return -1;
7872 res = g_utf8_collate(str1, str2);
7873 g_free(str1);
7874 g_free(str2);
7875 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7878 static gint summary_cmp_by_simplified_subject
7879 (GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2)
7881 const FolderItemPrefs *prefs;
7882 const gchar *str1, *str2;
7883 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1;
7884 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2;
7885 const MsgInfo *msginfo1 = r1->data;
7886 const MsgInfo *msginfo2 = r2->data;
7887 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview");
7888 gint res;
7890 cm_return_val_if_fail(sv, -1);
7891 cm_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1);
7893 if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) {
7894 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7895 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text;
7896 } else {
7897 str1 = msginfo1->subject;
7898 str2 = msginfo2->subject;
7901 if (!str1)
7902 return str2 != NULL;
7904 if (!str2)
7905 return -1;
7907 prefs = msginfo1->folder->prefs;
7908 if (!prefs)
7909 prefs = msginfo2->folder->prefs;
7910 if (!prefs)
7911 return -1;
7913 res = subject_compare_for_sort(str1, str2);
7914 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2);
7917 static gint summary_cmp_by_score(GtkCMCList *clist,
7918 gconstpointer ptr1, gconstpointer ptr2)
7920 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data;
7921 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data;
7922 int diff;
7924 /* if score are equal, sort by date */
7926 diff = msginfo1->score - msginfo2->score;
7927 if (diff != 0)
7928 return diff;
7929 else
7930 return summary_cmp_by_date(clist, ptr1, ptr2);
7933 static void summary_ignore_thread_func_mark_unread(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7935 MsgInfo *msginfo;
7937 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7938 cm_return_if_fail(msginfo);
7940 summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
7941 summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
7943 debug_print("Message %d is marked as ignore thread\n", msginfo->msgnum);
7946 static void summary_ignore_thread_func_set_row(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7948 SummaryView *summaryview = (SummaryView *) data;
7949 MsgInfo *msginfo;
7951 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7952 cm_return_if_fail(msginfo);
7954 summary_set_row_marks(summaryview, row);
7955 debug_print("Message %d update in row view\n", msginfo->msgnum);
7958 void summary_ignore_thread(SummaryView *summaryview)
7960 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7961 GList *cur;
7962 gboolean froze = FALSE;
7964 START_LONG_OPERATION(summaryview, FALSE);
7965 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7966 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7967 GTK_CMCTREE_FUNC(summary_ignore_thread_func_mark_unread),
7968 summaryview);
7970 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
7971 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
7972 GTK_CMCTREE_FUNC(summary_ignore_thread_func_set_row),
7973 summaryview);
7975 END_LONG_OPERATION(summaryview);
7977 summary_status_show(summaryview);
7980 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
7982 SummaryView *summaryview = (SummaryView *) data;
7983 MsgInfo *msginfo;
7985 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
7986 cm_return_if_fail(msginfo);
7988 summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0);
7990 summary_set_row_marks(summaryview, row);
7991 debug_print("Message %d is marked as unignore thread\n",
7992 msginfo->msgnum);
7995 void summary_unignore_thread(SummaryView *summaryview)
7997 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
7998 GList *cur;
7999 gboolean froze = FALSE;
8001 START_LONG_OPERATION(summaryview, FALSE);
8002 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8003 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8004 GTK_CMCTREE_FUNC(summary_unignore_thread_func),
8005 summaryview);
8007 END_LONG_OPERATION(summaryview);
8009 summary_status_show(summaryview);
8012 static void summary_check_ignore_thread_func
8013 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8015 MsgInfo *msginfo;
8016 gint *found_ignore = (gint *) data;
8018 if (*found_ignore) return;
8019 else {
8020 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8021 *found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags);
8025 void summary_toggle_ignore_thread(SummaryView *summaryview)
8027 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8028 GList *cur;
8029 gint found_ignore = 0;
8031 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8032 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8033 GTK_CMCTREE_FUNC(summary_check_ignore_thread_func),
8034 &found_ignore);
8036 if (found_ignore)
8037 summary_unignore_thread(summaryview);
8038 else
8039 summary_ignore_thread(summaryview);
8042 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8044 SummaryView *summaryview = (SummaryView *) data;
8045 MsgInfo *msginfo;
8047 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8048 cm_return_if_fail(msginfo);
8050 summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0);
8052 summary_set_row_marks(summaryview, row);
8053 debug_print("Message %d is marked as watch thread\n",
8054 msginfo->msgnum);
8057 void summary_watch_thread(SummaryView *summaryview)
8059 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8060 GList *cur;
8061 gboolean froze = FALSE;
8063 START_LONG_OPERATION(summaryview, FALSE);
8064 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8065 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8066 GTK_CMCTREE_FUNC(summary_watch_thread_func),
8067 summaryview);
8069 END_LONG_OPERATION(summaryview);
8071 summary_status_show(summaryview);
8074 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8076 SummaryView *summaryview = (SummaryView *) data;
8077 MsgInfo *msginfo;
8079 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8080 cm_return_if_fail(msginfo);
8082 summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0);
8084 summary_set_row_marks(summaryview, row);
8085 debug_print("Message %d is marked as unwatch thread\n",
8086 msginfo->msgnum);
8089 void summary_unwatch_thread(SummaryView *summaryview)
8091 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8092 GList *cur;
8093 gboolean froze = FALSE;
8095 START_LONG_OPERATION(summaryview, FALSE);
8096 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8097 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8098 GTK_CMCTREE_FUNC(summary_unwatch_thread_func),
8099 summaryview);
8101 END_LONG_OPERATION(summaryview);
8103 summary_status_show(summaryview);
8106 static void summary_check_watch_thread_func
8107 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data)
8109 MsgInfo *msginfo;
8110 gint *found_watch = (gint *) data;
8112 if (*found_watch) return;
8113 else {
8114 msginfo = gtk_cmctree_node_get_row_data(ctree, row);
8115 *found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags);
8119 void summary_toggle_watch_thread(SummaryView *summaryview)
8121 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree);
8122 GList *cur;
8123 gint found_watch = 0;
8125 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next)
8126 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data),
8127 GTK_CMCTREE_FUNC(summary_check_watch_thread_func),
8128 &found_watch);
8130 if (found_watch)
8131 summary_unwatch_thread(summaryview);
8132 else
8133 summary_watch_thread(summaryview);
8137 void summary_toggle_show_read_messages(SummaryView *summaryview)
8139 FolderItemUpdateData source;
8140 if (summaryview->folder_item->hide_read_msgs)
8141 summaryview->folder_item->hide_read_msgs = 0;
8142 else
8143 summaryview->folder_item->hide_read_msgs = 1;
8145 source.item = summaryview->folder_item;
8146 source.update_flags = F_ITEM_UPDATE_NAME;
8147 source.msg = NULL;
8148 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8149 summary_show(summaryview, summaryview->folder_item, FALSE);
8152 void summary_toggle_show_del_messages(SummaryView *summaryview)
8154 FolderItemUpdateData source;
8155 if (summaryview->folder_item->hide_del_msgs)
8156 summaryview->folder_item->hide_del_msgs = 0;
8157 else
8158 summaryview->folder_item->hide_del_msgs = 1;
8160 source.item = summaryview->folder_item;
8161 source.update_flags = F_ITEM_UPDATE_NAME;
8162 source.msg = NULL;
8163 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8164 summary_show(summaryview, summaryview->folder_item, FALSE);
8167 void summary_toggle_show_read_threads(SummaryView *summaryview)
8169 FolderItemUpdateData source;
8170 if (summaryview->folder_item->hide_read_threads)
8171 summaryview->folder_item->hide_read_threads = 0;
8172 else
8173 summaryview->folder_item->hide_read_threads = 1;
8175 source.item = summaryview->folder_item;
8176 source.update_flags = F_ITEM_UPDATE_NAME;
8177 source.msg = NULL;
8178 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
8179 summary_show(summaryview, summaryview->folder_item, FALSE);
8182 static void summary_set_hide_menu (SummaryView *summaryview,
8183 const gchar *menu_item,
8184 guint action)
8186 GtkWidget *widget;
8188 widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, menu_item);
8189 cm_return_if_fail(widget != NULL);
8191 g_object_set_data(G_OBJECT(widget), "dont_toggle",
8192 GINT_TO_POINTER(1));
8193 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action);
8194 g_object_set_data(G_OBJECT(widget), "dont_toggle",
8195 GINT_TO_POINTER(0));
8198 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview)
8200 GtkWidget *pixmap;
8202 stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm);
8203 stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm);
8204 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
8205 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
8206 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm);
8207 stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm);
8208 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm);
8209 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm);
8210 stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm);
8211 stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm);
8212 stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm);
8213 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm);
8214 stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm);
8215 stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm);
8216 stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm);
8217 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm);
8218 stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm);
8219 stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm);
8220 stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm);
8222 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8224 pixmap = stock_pixmap_widget(STOCK_PIXMAP_QUICKSEARCH);
8225 gtk_container_remove (GTK_CONTAINER(summaryview->toggle_search),
8226 summaryview->quick_search_pixmap);
8227 gtk_container_add(GTK_CONTAINER(summaryview->toggle_search), pixmap);
8228 gtk_widget_show(pixmap);
8229 summaryview->quick_search_pixmap = pixmap;
8231 #ifdef GENERIC_UMPC
8232 pixmap = stock_pixmap_widget(STOCK_PIXMAP_SELECTION);
8233 gtk_container_remove (GTK_CONTAINER(summaryview->multiple_sel_togbtn),
8234 summaryview->multiple_sel_image);
8235 gtk_container_add(GTK_CONTAINER(summaryview->multiple_sel_togbtn), pixmap);
8236 gtk_widget_show(pixmap);
8237 summaryview->multiple_sel_togbtn = pixmap;
8238 #endif
8240 folderview_unselect(summaryview->folderview);
8241 folderview_select(summaryview->folderview, summaryview->folder_item);
8242 summary_set_column_titles(summaryview);
8245 void summary_reflect_prefs_custom_colors(SummaryView *summaryview)
8247 GtkMenuShell *menu;
8248 GList *children, *cur;
8250 /* re-create colorlabel submenu */
8251 menu = GTK_MENU_SHELL(summaryview->colorlabel_menu);
8252 cm_return_if_fail(menu != NULL);
8254 /* clear items. get item pointers. */
8255 children = gtk_container_get_children(GTK_CONTAINER(menu));
8256 for (cur = children; cur != NULL && cur->data != NULL; cur = cur->next) {
8257 g_signal_handlers_disconnect_matched
8258 (gtk_ui_manager_get_accel_group(summaryview->mainwin->ui_manager),
8259 G_SIGNAL_MATCH_DATA|G_SIGNAL_MATCH_FUNC,
8260 0, 0, NULL, mainwin_accel_changed_cb, cur->data);
8261 gtk_menu_item_set_submenu(GTK_MENU_ITEM(cur->data), NULL);
8263 g_list_free(children);
8264 summary_colorlabel_menu_create(summaryview, TRUE);
8268 * Harvest addresses for selected messages in summary view.
8270 void summary_harvest_address(SummaryView *summaryview)
8272 GtkCMCTree *ctree = GTK_CMCTREE( summaryview->ctree );
8273 GList *cur;
8274 GList *msgList;
8275 MsgInfo *msginfo;
8277 msgList = NULL;
8278 for( cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next ) {
8279 msginfo = gtk_cmctree_node_get_row_data( ctree, GTK_CMCTREE_NODE(cur->data) );
8280 if (!msginfo)
8281 continue;
8282 msgList = g_list_append( msgList, GUINT_TO_POINTER( msginfo->msgnum ) );
8285 addressbook_harvest( summaryview->folder_item, TRUE, msgList );
8287 g_list_free( msgList );
8290 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp)
8292 int err;
8293 gchar buf[BUFFSIZE];
8294 regex_t *preg = NULL;
8296 preg = g_new0(regex_t, 1);
8298 err = string_match_precompile(simplify_subject_regexp,
8299 preg, REG_EXTENDED);
8300 if (err) {
8301 regerror(err, preg, buf, BUFFSIZE);
8302 alertpanel_error(_("Regular expression (regexp) error:\n%s"), buf);
8303 g_free(preg);
8304 preg = NULL;
8307 return preg;
8309 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item)
8311 FolderSortKey sort_key;
8312 FolderSortType sort_type;
8313 cm_return_if_fail(summaryview != NULL);
8314 cm_return_if_fail(item != NULL);
8316 /* Subject simplification */
8317 if(summaryview->simplify_subject_preg) {
8318 regfree(summaryview->simplify_subject_preg);
8319 g_free(summaryview->simplify_subject_preg);
8320 summaryview->simplify_subject_preg = NULL;
8322 if(item->prefs && item->prefs->simplify_subject_regexp &&
8323 item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject)
8324 summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp);
8326 /* Sorting */
8327 sort_key = item->sort_key;
8328 sort_type = item->sort_type;
8330 folder_get_sort_type(item->folder, &sort_key, &sort_type);
8332 summaryview->sort_key = sort_key;
8333 summaryview->sort_type = sort_type;
8335 /* Threading */
8336 summaryview->threaded = item->threaded;
8337 summaryview->thread_collapsed = item->thread_collapsed;
8339 /* Scoring */
8342 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item)
8344 /* Sorting */
8345 item->sort_key = summaryview->sort_key;
8346 item->sort_type = summaryview->sort_type;
8348 /* Threading */
8349 item->threaded = summaryview->threaded;
8350 item->thread_collapsed = summaryview->thread_collapsed;
8353 static gboolean summary_update_msg(gpointer source, gpointer data)
8355 MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source;
8356 SummaryView *summaryview = (SummaryView *)data;
8357 GtkCMCTreeNode *node;
8359 cm_return_val_if_fail(msginfo_update != NULL, TRUE);
8360 cm_return_val_if_fail(summaryview != NULL, FALSE);
8362 if (msginfo_update->msginfo->folder != summaryview->folder_item)
8363 return FALSE;
8365 if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) {
8366 node = gtk_cmctree_find_by_row_data(
8367 GTK_CMCTREE(summaryview->ctree), NULL,
8368 msginfo_update->msginfo);
8370 if (node)
8371 summary_set_row_marks(summaryview, node);
8374 return FALSE;
8377 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item)
8379 guint new, unread, unreadmarked, marked, total;
8380 guint replied, forwarded, locked, ignored, watched;
8381 static gboolean tips_initialized = FALSE;
8383 if (prefs_common.layout_mode != SMALL_LAYOUT) {
8384 if (tips_initialized) {
8385 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8386 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8387 NULL);
8388 tips_initialized = FALSE;
8390 return;
8392 folder_count_total_msgs(&new, &unread, &unreadmarked, &marked, &total,
8393 &replied, &forwarded, &locked, &ignored,
8394 &watched);
8395 if (removed_item) {
8396 total -= removed_item->total_msgs;
8397 new -= removed_item->new_msgs;
8398 unread -= removed_item->unread_msgs;
8401 if (new > 0 || unread > 0) {
8402 tips_initialized = TRUE;
8403 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN_HRM);
8404 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8405 _("Go back to the folder list (You have unread messages)"));
8406 } else {
8407 tips_initialized = TRUE;
8408 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN);
8409 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox,
8410 _("Go back to the folder list"));
8414 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data)
8416 FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source;
8417 SummaryView *summaryview = (SummaryView *)data;
8419 cm_return_val_if_fail(hookdata != NULL, FALSE);
8420 cm_return_val_if_fail(hookdata->item != NULL, FALSE);
8421 cm_return_val_if_fail(summaryview != NULL, FALSE);
8423 if (hookdata->item == summaryview->folder_item &&
8424 hookdata->update_flags & F_ITEM_UPDATE_NAME) {
8425 gchar *name = folder_item_get_name(hookdata->item);
8426 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name);
8427 g_free(name);
8429 summary_update_unread(summaryview, NULL);
8431 return FALSE;
8434 static gboolean summary_update_folder_hook(gpointer source, gpointer data)
8436 FolderUpdateData *hookdata;
8437 SummaryView *summaryview = (SummaryView *)data;
8438 hookdata = source;
8439 if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
8440 summary_update_unread(summaryview, hookdata->item);
8441 } else
8442 summary_update_unread(summaryview, NULL);
8444 return FALSE;
8448 *\brief change summaryview to display your answer(s) to a message
8450 *\param summaryview The SummaryView ;)
8451 *\param msginfo The message for which answers are searched
8454 static void summary_find_answers (SummaryView *summaryview, MsgInfo *msg)
8456 FolderItem *sent_folder = NULL;
8457 PrefsAccount *account = NULL;
8458 GtkCMCTreeNode *node = NULL;
8459 char *buf = NULL;
8460 if (msg == NULL || msg->msgid == NULL)
8461 return;
8463 account = account_get_reply_account(msg, prefs_common.reply_account_autosel);
8464 if (account == NULL)
8465 return;
8466 sent_folder = account_get_special_folder
8467 (account, F_OUTBOX);
8469 buf = g_strdup_printf("inreplyto matchcase \"%s\"", msg->msgid);
8471 if (sent_folder != summaryview->folder_item) {
8472 folderview_select(summaryview->mainwin->folderview, sent_folder);
8475 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summaryview->toggle_search), TRUE);
8477 quicksearch_set(summaryview->quicksearch, ADVANCED_SEARCH_EXTENDED, buf);
8478 g_free(buf);
8480 node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0);
8481 if (node)
8482 summary_select_node(summaryview, node, OPEN_SELECTED_ON_SEARCH_RESULTS);
8485 gint summaryview_export_mbox_list(SummaryView *summaryview)
8486 /* return values: -2 skipped, -1 error, 0 OK */
8488 GSList *list = summary_get_selected_msg_list(summaryview);
8489 gchar *mbox = filesel_select_file_save(_("Export to mbox file"), NULL);
8490 gint ret;
8492 if (mbox == NULL)
8493 return -2;
8494 if (list == NULL)
8495 return -1;
8497 ret = export_list_to_mbox(list, mbox);
8499 g_slist_free(list);
8500 g_free(mbox);
8502 return ret;
8505 void summaryview_lock(SummaryView *summaryview, FolderItem *item)
8507 if (!summaryview || !summaryview->folder_item || !item) {
8508 return;
8511 if (summaryview->folder_item->folder == item->folder) {
8512 gtk_widget_set_sensitive(summaryview->ctree, FALSE);
8515 void summaryview_unlock(SummaryView *summaryview, FolderItem *item)
8517 gtk_widget_set_sensitive(summaryview->ctree, TRUE);
8520 static void summary_reedit_cb(GtkAction *gaction, gpointer data)
8522 SummaryView *summaryview = (SummaryView *)data;
8523 summary_reedit(summaryview);
8526 #define DO_ACTION(name, act) { if (!strcmp(a_name, name)) action = act; }
8527 static void summary_reply_cb(GtkAction *gaction, gpointer data)
8529 SummaryView *summaryview = (SummaryView *)data;
8530 GSList *msginfo_list = NULL;
8531 gint action = COMPOSE_REPLY;
8532 const gchar *a_name = gtk_action_get_name(gaction);
8534 DO_ACTION("SummaryViewPopup/Reply", COMPOSE_REPLY);
8535 DO_ACTION("SummaryViewPopup/ReplyTo/All", COMPOSE_REPLY_TO_ALL);
8536 DO_ACTION("SummaryViewPopup/ReplyTo/Sender", COMPOSE_REPLY_TO_SENDER);
8537 DO_ACTION("SummaryViewPopup/ReplyTo/List", COMPOSE_REPLY_TO_LIST);
8538 DO_ACTION("SummaryViewPopup/Forward", COMPOSE_FORWARD_INLINE);
8539 DO_ACTION("SummaryViewPopup/ForwardAtt", COMPOSE_FORWARD_AS_ATTACH);
8540 DO_ACTION("SummaryViewPopup/Redirect", COMPOSE_REDIRECT);
8542 msginfo_list = summary_get_selection(summaryview);
8543 cm_return_if_fail(msginfo_list != NULL);
8544 compose_reply_from_messageview(NULL, msginfo_list, action);
8545 g_slist_free(msginfo_list);
8548 gboolean summary_is_opened_message_selected(SummaryView *summaryview)
8550 GList *sel = NULL;
8552 cm_return_val_if_fail(summaryview != NULL, FALSE);
8554 sel = GTK_CMCLIST(summaryview->ctree)->selection;
8556 if (summaryview->displayed == NULL || sel == NULL) {
8557 return FALSE;
8560 for ( ; sel != NULL; sel = sel->next) {
8561 if (summaryview->displayed == GTK_CMCTREE_NODE(sel->data)) {
8562 return TRUE;
8565 return FALSE;
8568 gboolean summary_has_opened_message(SummaryView *summaryview)
8570 cm_return_val_if_fail(summaryview != NULL, FALSE);
8572 return (summaryview->displayed != NULL);
8575 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data)
8577 SummaryView *summaryview = (SummaryView *)data;
8578 gboolean sorting_lock = prefs_common_get_prefs()->summary_col_lock;
8580 if (summaryview->header_menu_lock)
8581 return;
8583 debug_print("%slocking summaryview columns\n",
8584 sorting_lock ? "un" : "");
8585 prefs_common_get_prefs()->summary_col_lock = !sorting_lock;
8588 static void summary_header_set_displayed_columns_cb(GtkAction *gaction,
8589 gpointer data)
8591 SummaryView *summaryview = (SummaryView *)data;
8593 if (summaryview->header_menu_lock)
8594 return;
8596 prefs_summary_column_open();