summaryview.c (200606B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 1999-2025 the Claws Mail team and Hiroyuki Yamamoto 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include "defs.h" 20 21 #include <glib.h> 22 #include <glib/gi18n.h> 23 #include <gdk/gdkkeysyms.h> 24 #include <gtk/gtk.h> 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <ctype.h> 30 31 #include "main.h" 32 #include "menu.h" 33 #include "mbox.h" 34 #include "mainwindow.h" 35 #include "folderview.h" 36 #include "summaryview.h" 37 #include "messageview.h" 38 #include "mimeview.h" 39 #include "foldersel.h" 40 #include "procmsg.h" 41 #include "procheader.h" 42 #include "sourcewindow.h" 43 #include "prefs_common.h" 44 #include "prefs_summary_column.h" 45 #include "prefs_summary_open.h" 46 #include "account.h" 47 #include "compose.h" 48 #include "file-utils.h" 49 #include "utils.h" 50 #include "gtkutils.h" 51 #include "stock_pixmap.h" 52 #include "filesel.h" 53 #include "alertpanel.h" 54 #include "inputdialog.h" 55 #include "statusbar.h" 56 #include "folder.h" 57 #include "inc.h" 58 #include "imap.h" 59 #include "addressbook.h" 60 #include "addr_compl.h" 61 #include "folder_item_prefs.h" 62 #include "string_match.h" 63 #include "toolbar.h" 64 #include "hooks.h" 65 #include "folderutils.h" 66 #include "log.h" 67 #include "manual.h" 68 #include "manage_window.h" 69 70 #define SUMMARY_COL_MARK_WIDTH 10 71 #define SUMMARY_COL_STATUS_WIDTH 13 72 #define SUMMARY_COL_LOCKED_WIDTH 13 73 #define SUMMARY_COL_MIME_WIDTH 11 74 75 static int normal_row_height = -1; 76 static GtkStyle *bold_style; 77 78 static GdkPixbuf *markxpm; 79 static GdkPixbuf *deletedxpm; 80 static GdkPixbuf *movedxpm; 81 static GdkPixbuf *copiedxpm; 82 83 static GdkPixbuf *newxpm; 84 static GdkPixbuf *unreadxpm; 85 static GdkPixbuf *repliedxpm; 86 static GdkPixbuf *forwardedxpm; 87 static GdkPixbuf *repliedandforwardedxpm; 88 static GdkPixbuf *ignorethreadxpm; 89 static GdkPixbuf *watchthreadxpm; 90 static GdkPixbuf *lockedxpm; 91 static GdkPixbuf *spamxpm; 92 93 static GdkPixbuf *clipxpm; 94 static GdkPixbuf *keyxpm; 95 static GdkPixbuf *clipkeyxpm; 96 static GdkPixbuf *keysignxpm; 97 static GdkPixbuf *gpgsignedxpm; 98 static GdkPixbuf *clipgpgsignedxpm; 99 100 static void summary_free_msginfo_func (GtkCMCTree *ctree, 101 GtkCMCTreeNode *node, 102 gpointer data); 103 static void summary_set_marks_func (GtkCMCTree *ctree, 104 GtkCMCTreeNode *node, 105 gpointer data); 106 107 void summary_set_menu_sensitive (SummaryView *summaryview); 108 guint summary_get_msgnum (SummaryView *summaryview, 109 GtkCMCTreeNode *node); 110 111 112 static void summary_set_hide_menu (SummaryView *summaryview, 113 const gchar *menu_item, 114 guint action); 115 116 static GtkCMCTreeNode *summary_find_prev_msg 117 (SummaryView *summaryview, 118 GtkCMCTreeNode *current_node, 119 gboolean start_from_prev); 120 static GtkCMCTreeNode *summary_find_next_msg 121 (SummaryView *summaryview, 122 GtkCMCTreeNode *current_node, 123 gboolean start_from_next); 124 125 static GtkCMCTreeNode *summary_find_prev_flagged_msg 126 (SummaryView *summaryview, 127 GtkCMCTreeNode *current_node, 128 MsgPermFlags flags, 129 gboolean start_from_prev); 130 static GtkCMCTreeNode *summary_find_next_flagged_msg 131 (SummaryView *summaryview, 132 GtkCMCTreeNode *current_node, 133 MsgPermFlags flags, 134 gboolean start_from_next); 135 136 static GtkCMCTreeNode *summary_find_msg_by_msgnum 137 (SummaryView *summaryview, 138 guint msgnum); 139 140 static void summary_update_status (SummaryView *summaryview); 141 142 /* display functions */ 143 static void summary_status_show (SummaryView *summaryview); 144 static void summary_set_column_titles (SummaryView *summaryview); 145 static void summary_set_ctree_from_list (SummaryView *summaryview, 146 GSList *mlist, 147 guint selected_msgnum); 148 static inline void summary_set_header (SummaryView *summaryview, 149 gchar *text[], 150 MsgInfo *msginfo); 151 static void summary_display_msg (SummaryView *summaryview, 152 GtkCMCTreeNode *row); 153 static void summary_display_msg_full (SummaryView *summaryview, 154 GtkCMCTreeNode *row, 155 gboolean new_window, 156 gboolean all_headers, 157 gboolean from_sep_msgview); 158 static void summary_set_row_marks (SummaryView *summaryview, 159 GtkCMCTreeNode *row); 160 161 /* message handling */ 162 static void summary_mark_row (SummaryView *summaryview, 163 GtkCMCTreeNode *row); 164 static void summary_lock_row (SummaryView *summaryview, 165 GtkCMCTreeNode *row); 166 static void summary_unlock_row (SummaryView *summaryview, 167 GtkCMCTreeNode *row); 168 static void summary_mark_row_as_read (SummaryView *summaryview, 169 GtkCMCTreeNode *row); 170 static void summary_mark_row_as_unread (SummaryView *summaryview, 171 GtkCMCTreeNode *row); 172 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed); 173 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed); 174 static void summary_delete_row (SummaryView *summaryview, 175 GtkCMCTreeNode *row); 176 static void summary_unmark_row (SummaryView *summaryview, 177 GtkCMCTreeNode *row); 178 static void summary_move_row_to (SummaryView *summaryview, 179 GtkCMCTreeNode *row, 180 FolderItem *to_folder); 181 static void summary_copy_row_to (SummaryView *summaryview, 182 GtkCMCTreeNode *row, 183 FolderItem *to_folder); 184 185 static gint summary_execute_move (SummaryView *summaryview); 186 static void summary_execute_move_func (GtkCMCTree *ctree, 187 GtkCMCTreeNode *node, 188 gpointer data); 189 static void summary_execute_copy (SummaryView *summaryview); 190 static void summary_execute_copy_func (GtkCMCTree *ctree, 191 GtkCMCTreeNode *node, 192 gpointer data); 193 static void summary_execute_delete (SummaryView *summaryview); 194 static void summary_execute_delete_func (GtkCMCTree *ctree, 195 GtkCMCTreeNode *node, 196 gpointer data); 197 static void summary_execute_expunge (SummaryView *summaryview); 198 199 static void summary_thread_init (SummaryView *summaryview); 200 201 static void summary_unthread_for_exec (SummaryView *summaryview); 202 static void summary_unthread_for_exec_func (GtkCMCTree *ctree, 203 GtkCMCTreeNode *node, 204 gpointer data); 205 206 void summary_simplify_subject(SummaryView *summaryview, gchar * rexp, 207 GSList * mlist); 208 209 static GtkWidget *summary_ctree_create (SummaryView *summaryview); 210 211 /* callback functions */ 212 static gint summary_toggle_pressed (GtkWidget *eventbox, 213 GdkEventButton *event, 214 SummaryView *summaryview); 215 216 static gint summary_folder_eventbox_pressed 217 (GtkWidget *eventbox, 218 GdkEventButton *event, 219 SummaryView *summaryview); 220 static gboolean summary_button_pressed (GtkWidget *ctree, 221 GdkEventButton *event, 222 SummaryView *summaryview); 223 static gboolean summary_button_released (GtkWidget *ctree, 224 GdkEventButton *event, 225 SummaryView *summaryview); 226 static gboolean summary_key_pressed (GtkWidget *ctree, 227 GdkEventKey *event, 228 SummaryView *summaryview); 229 static void summary_tree_expanded (GtkCMCTree *ctree, 230 GtkCMCTreeNode *node, 231 SummaryView *summaryview); 232 static void summary_tree_collapsed (GtkCMCTree *ctree, 233 GtkCMCTreeNode *node, 234 SummaryView *summaryview); 235 static void summary_selected (GtkCMCTree *ctree, 236 GtkCMCTreeNode *row, 237 gint column, 238 SummaryView *summaryview); 239 static void summary_unselected (GtkCMCTree *ctree, 240 GtkCMCTreeNode *row, 241 gint column, 242 SummaryView *summaryview); 243 static void summary_col_resized (GtkCMCList *clist, 244 gint column, 245 gint width, 246 SummaryView *summaryview); 247 static void summary_mark_clicked (GtkWidget *button, 248 SummaryView *summaryview); 249 static void summary_status_clicked (GtkWidget *button, 250 SummaryView *summaryview); 251 static void summary_mime_clicked (GtkWidget *button, 252 SummaryView *summaryview); 253 static void summary_num_clicked (GtkWidget *button, 254 SummaryView *summaryview); 255 static void summary_score_clicked (GtkWidget *button, 256 SummaryView *summaryview); 257 static void summary_size_clicked (GtkWidget *button, 258 SummaryView *summaryview); 259 static void summary_date_clicked (GtkWidget *button, 260 SummaryView *summaryview); 261 static void summary_from_clicked (GtkWidget *button, 262 SummaryView *summaryview); 263 static void summary_to_clicked (GtkWidget *button, 264 SummaryView *summaryview); 265 static void summary_subject_clicked (GtkWidget *button, 266 SummaryView *summaryview); 267 static void summary_score_clicked (GtkWidget *button, 268 SummaryView *summaryview); 269 static void summary_locked_clicked (GtkWidget *button, 270 SummaryView *summaryview); 271 272 /* custom compare functions for sorting */ 273 static gint summary_cmp_by_mark (GtkCMCList *clist, 274 gconstpointer ptr1, 275 gconstpointer ptr2); 276 static gint summary_cmp_by_status (GtkCMCList *clist, 277 gconstpointer ptr1, 278 gconstpointer ptr2); 279 static gint summary_cmp_by_mime (GtkCMCList *clist, 280 gconstpointer ptr1, 281 gconstpointer ptr2); 282 static gint summary_cmp_by_num (GtkCMCList *clist, 283 gconstpointer ptr1, 284 gconstpointer ptr2); 285 static gint summary_cmp_by_size (GtkCMCList *clist, 286 gconstpointer ptr1, 287 gconstpointer ptr2); 288 static gint summary_cmp_by_date (GtkCMCList *clist, 289 gconstpointer ptr1, 290 gconstpointer ptr2); 291 static gint summary_cmp_by_thread_date (GtkCMCList *clist, 292 gconstpointer ptr1, 293 gconstpointer ptr2); 294 static gint summary_cmp_by_from (GtkCMCList *clist, 295 gconstpointer ptr1, 296 gconstpointer ptr2); 297 static gint summary_cmp_by_simplified_subject 298 (GtkCMCList *clist, 299 gconstpointer ptr1, 300 gconstpointer ptr2); 301 static gint summary_cmp_by_score (GtkCMCList *clist, 302 gconstpointer ptr1, 303 gconstpointer ptr2); 304 static gint summary_cmp_by_label (GtkCMCList *clist, 305 gconstpointer ptr1, 306 gconstpointer ptr2); 307 static gint summary_cmp_by_to (GtkCMCList *clist, 308 gconstpointer ptr1, 309 gconstpointer ptr2); 310 static gint summary_cmp_by_subject (GtkCMCList *clist, 311 gconstpointer ptr1, 312 gconstpointer ptr2); 313 static gint summary_cmp_by_locked (GtkCMCList *clist, 314 gconstpointer ptr1, 315 gconstpointer ptr2); 316 317 static void summary_find_answers (SummaryView *summaryview, 318 MsgInfo *msg); 319 320 static gboolean summary_update_msg (gpointer source, gpointer data); 321 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data); 322 static gboolean summary_update_folder_hook(gpointer source, gpointer data); 323 static void summary_thread_build(SummaryView *summaryview); 324 325 GtkTargetEntry summary_drag_types[3] = 326 { 327 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}, 328 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY}, 329 {"claws-mail/msg-path-list", 0, TARGET_MAIL_CM_PATH_LIST}, 330 }; 331 332 static void summary_reedit_cb(GtkAction *gaction, gpointer data); 333 static void summary_reply_cb(GtkAction *gaction, gpointer data); 334 335 /* Only submenus and specifically-handled menu entries here */ 336 static GtkActionEntry summary_popup_entries[] = 337 { 338 {"SummaryViewPopup", NULL, "SummaryViewPopup", NULL, NULL, NULL }, 339 {"SummaryViewPopup/Reedit", NULL, N_("Re-edit"), NULL, NULL, G_CALLBACK(summary_reedit_cb) }, 340 {"SummaryViewPopup/Reply", NULL, N_("_Reply"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY */ 341 {"SummaryViewPopup/ReplyTo", NULL, N_("Repl_y to"), NULL, NULL, NULL }, 342 {"SummaryViewPopup/ReplyTo/All", NULL, N_("_All"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_ALL */ 343 {"SummaryViewPopup/ReplyTo/Sender", NULL, N_("_Sender"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_SENDER */ 344 {"SummaryViewPopup/ReplyTo/List", NULL, N_("Mailing _list"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REPLY_TO_LIST */ 345 346 {"SummaryViewPopup/Forward", NULL, N_("_Forward"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_INLINE */ 347 {"SummaryViewPopup/ForwardAtt", NULL, N_("For_ward as attachment"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_FORWARD_AS_ATTACH */ 348 {"SummaryViewPopup/Redirect", NULL, N_("R_edirect"), NULL, NULL, G_CALLBACK(summary_reply_cb) }, /* COMPOSE_REDIRECT */ 349 {"SummaryViewPopup/Marks", NULL, N_("_Marks"), NULL, NULL, NULL }, 350 {"SummaryViewPopup/View", NULL, N_("_View"), NULL, NULL, NULL }, 351 }; 352 353 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data); 354 static void summary_header_set_displayed_columns_cb(GtkAction *gaction, gpointer data); 355 356 static GtkActionEntry summary_header_popup_entries[] = 357 { 358 {"SummaryViewHeaderPopup", NULL, "SummaryViewHeaderPopup", NULL, NULL, NULL }, 359 {"SummaryViewHeaderPopup/SetDisplayedColumns", NULL, N_("_Set displayed columns"), NULL, NULL, G_CALLBACK(summary_header_set_displayed_columns_cb) } 360 }; 361 362 static GtkToggleActionEntry summary_header_popup_toggle_entries[] = 363 { 364 {"SummaryViewHeaderPopup/LockColumnHeaders", NULL, N_("_Lock column headers"), NULL, NULL, G_CALLBACK(summary_header_lock_sorting_cb), FALSE } 365 }; 366 367 static const gchar *const col_label[N_SUMMARY_COLS] = { 368 "", /* S_COL_MARK */ 369 N_("S"), /* S_COL_STATUS */ 370 "", /* S_COL_MIME */ 371 N_("Subject"), /* S_COL_SUBJECT */ 372 N_("From"), /* S_COL_FROM */ 373 N_("To"), /* S_COL_TO */ 374 N_("Date"), /* S_COL_DATE */ 375 N_("Size"), /* S_COL_SIZE */ 376 N_("#"), /* S_COL_NUMBER */ 377 N_("Score"), /* S_COL_SCORE */ 378 "", /* S_COL_LOCKED */ 379 }; 380 381 void summary_freeze(SummaryView *summaryview) 382 { 383 if (summaryview) 384 gtk_cmclist_freeze(GTK_CMCLIST(summaryview->ctree)); 385 } 386 387 void summary_thaw(SummaryView *summaryview) 388 { 389 if (summaryview) 390 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree)); 391 } 392 393 void summary_thaw_with_status(SummaryView *summaryview) 394 { 395 if (summaryview) { 396 summary_status_show(summaryview); 397 gtk_cmclist_thaw(GTK_CMCLIST(summaryview->ctree)); 398 } 399 } 400 401 void summary_grab_focus(SummaryView *summaryview) 402 { 403 if (summaryview) 404 gtk_widget_grab_focus(summaryview->ctree); 405 } 406 407 GtkWidget *summary_get_main_widget(SummaryView *summaryview) 408 { 409 if (summaryview) 410 return summaryview->ctree; 411 else 412 return NULL; 413 } 414 415 #define START_LONG_OPERATION(summaryview,force_freeze) { \ 416 summary_lock(summaryview); \ 417 main_window_cursor_wait(summaryview->mainwin); \ 418 if (force_freeze || sc_g_list_bigger(GTK_CMCLIST(summaryview->ctree)->selection, 1)) {\ 419 froze = TRUE; \ 420 summary_freeze(summaryview); \ 421 } \ 422 folder_item_update_freeze(); \ 423 inc_lock(); \ 424 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, \ 425 summaryview->msginfo_update_callback_id); \ 426 } 427 #define END_LONG_OPERATION(summaryview) { \ 428 inc_unlock(); \ 429 folder_item_update_thaw(); \ 430 if (froze) \ 431 summary_thaw(summaryview); \ 432 main_window_cursor_normal(summaryview->mainwin); \ 433 summary_unlock(summaryview); \ 434 summaryview->msginfo_update_callback_id = \ 435 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, \ 436 summary_update_msg, (gpointer) summaryview); \ 437 } 438 439 SummaryView *summary_create(MainWindow *mainwin) 440 { 441 SummaryView *summaryview; 442 GtkWidget *vbox; 443 GtkWidget *scrolledwin; 444 GtkWidget *ctree; 445 GtkWidget *hbox; 446 GtkWidget *hbox_l; 447 GtkWidget *stat_box; 448 GtkWidget *stat_box2; 449 GtkWidget *stat_vbox; 450 GtkWidget *statlabel_folder; 451 GtkWidget *statlabel_select; 452 GtkWidget *statlabel_msgs; 453 GtkWidget *hbox_spc; 454 GtkWidget *toggle_eventbox; 455 GtkWidget *toggle_arrow; 456 457 debug_print("Creating summary view...\n"); 458 summaryview = g_new0(SummaryView, 1); 459 460 #define SUMMARY_VBOX_SPACING 3 461 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, SUMMARY_VBOX_SPACING); 462 gtk_widget_set_name(GTK_WIDGET(vbox), "summaryview"); 463 464 /* create status label */ 465 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 466 gtk_widget_show(hbox); 467 468 stat_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 469 gtk_widget_show(stat_vbox); 470 471 stat_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 472 gtk_widget_show(stat_box); 473 474 stat_box2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 475 gtk_widget_show(stat_box2); 476 477 gtk_box_pack_start(GTK_BOX(hbox), stat_vbox, TRUE, TRUE, 0); 478 gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box, TRUE, TRUE, 0); 479 gtk_box_pack_start(GTK_BOX(stat_vbox), stat_box2, TRUE, TRUE, 0); 480 481 hbox_l = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 482 gtk_widget_show(hbox_l); 483 gtk_box_pack_start(GTK_BOX(stat_box), hbox_l, TRUE, TRUE, 0); 484 485 statlabel_folder = gtk_label_new(""); 486 gtk_widget_show(statlabel_folder); 487 gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_folder, FALSE, FALSE, 2); 488 statlabel_select = gtk_label_new(""); 489 gtk_widget_show(statlabel_select); 490 gtk_box_pack_start(GTK_BOX(hbox_l), statlabel_select, FALSE, FALSE, 12); 491 492 /* toggle view button */ 493 toggle_eventbox = gtk_event_box_new(); 494 gtk_widget_show(toggle_eventbox); 495 496 gtk_box_pack_end(GTK_BOX(hbox), toggle_eventbox, FALSE, FALSE, HSPACING_NARROW); 497 498 toggle_arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_MENU); 499 gtk_widget_show(toggle_arrow); 500 gtk_container_add(GTK_CONTAINER(toggle_eventbox), toggle_arrow); 501 g_signal_connect(G_OBJECT(toggle_eventbox), "button_press_event", 502 G_CALLBACK(summary_toggle_pressed), 503 summaryview); 504 505 statlabel_msgs = gtk_label_new(""); 506 gtk_widget_show(statlabel_msgs); 507 gtk_box_pack_end(GTK_BOX(stat_box), statlabel_msgs, FALSE, FALSE, HSPACING_NARROW); 508 509 hbox_spc = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); 510 gtk_widget_show(hbox_spc); 511 gtk_box_pack_end(GTK_BOX(hbox), hbox_spc, FALSE, FALSE, 6); 512 513 scrolledwin = gtk_scrolled_window_new(NULL, NULL); 514 gtk_widget_show(scrolledwin); 515 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin), 516 GTK_POLICY_AUTOMATIC, 517 GTK_POLICY_AUTOMATIC); 518 summaryview->mainwidget_book = gtk_notebook_new(); 519 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE); 520 gtk_notebook_set_show_border(GTK_NOTEBOOK(summaryview->mainwidget_book), FALSE); 521 522 gtk_container_add(GTK_CONTAINER(summaryview->mainwidget_book), 523 scrolledwin); 524 gtk_box_pack_start(GTK_BOX(vbox), summaryview->mainwidget_book, TRUE, TRUE, 0); 525 526 ctree = summary_ctree_create(summaryview); 527 gtk_widget_show(ctree); 528 529 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 530 GTK_CMCLIST(ctree)->hadjustment); 531 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 532 GTK_CMCLIST(ctree)->vadjustment); 533 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree); 534 535 /* status label */ 536 gtk_widget_show_all(stat_vbox); 537 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); 538 539 /* create popup menu */ 540 541 gtk_action_group_add_actions(mainwin->action_group, 542 summary_popup_entries, 543 G_N_ELEMENTS(summary_popup_entries), 544 (gpointer)summaryview); 545 546 gtk_action_group_add_actions(mainwin->action_group, 547 summary_header_popup_entries, 548 G_N_ELEMENTS(summary_header_popup_entries), 549 (gpointer)summaryview); 550 551 gtk_action_group_add_toggle_actions(mainwin->action_group, 552 summary_header_popup_toggle_entries, 553 G_N_ELEMENTS(summary_header_popup_toggle_entries), 554 (gpointer)summaryview); 555 556 summaryview->ui_manager = gtk_ui_manager_new(); 557 summaryview->action_group = cm_menu_create_action_group_full(summaryview->ui_manager,"Menu", summary_popup_entries, 558 G_N_ELEMENTS(summary_popup_entries), (gpointer)summaryview); 559 560 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewPopup", "SummaryViewPopup", GTK_UI_MANAGER_MENU) 561 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reedit", "SummaryViewPopup/Reedit", GTK_UI_MANAGER_MENUITEM) 562 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Reply", "SummaryViewPopup/Reply", GTK_UI_MANAGER_MENUITEM) 563 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ReplyTo", "SummaryViewPopup/ReplyTo", GTK_UI_MANAGER_MENU) 564 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator1", "Message/---", GTK_UI_MANAGER_SEPARATOR) 565 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Forward", "SummaryViewPopup/Forward", GTK_UI_MANAGER_MENUITEM) 566 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "ForwardAtt", "SummaryViewPopup/ForwardAtt", GTK_UI_MANAGER_MENUITEM) 567 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Redirect", "SummaryViewPopup/Redirect", GTK_UI_MANAGER_MENUITEM) 568 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator2", "Message/---", GTK_UI_MANAGER_SEPARATOR) 569 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Move", "Message/Move", GTK_UI_MANAGER_MENUITEM) 570 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Copy", "Message/Copy", GTK_UI_MANAGER_MENUITEM) 571 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Trash", "Message/Trash", GTK_UI_MANAGER_MENUITEM) 572 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Delete", "Message/Delete", GTK_UI_MANAGER_MENUITEM) 573 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator3", "Message/---", GTK_UI_MANAGER_SEPARATOR) 574 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Marks", "SummaryViewPopup/Marks", GTK_UI_MANAGER_MENU) 575 576 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator4", "Message/---", GTK_UI_MANAGER_SEPARATOR) 577 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "AddSenderToAB", "Tools/AddSenderToAB", GTK_UI_MANAGER_MENUITEM) 578 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "Separator5", "Tools/---", GTK_UI_MANAGER_SEPARATOR) 579 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "View", "SummaryViewPopup/View", GTK_UI_MANAGER_MENU) 580 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup", "SaveAs", "File/SaveAs", GTK_UI_MANAGER_MENUITEM) 581 582 /* submenus - replyto */ 583 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "All", "SummaryViewPopup/ReplyTo/All", GTK_UI_MANAGER_MENUITEM) 584 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "Sender", "SummaryViewPopup/ReplyTo/Sender", GTK_UI_MANAGER_MENUITEM) 585 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/ReplyTo", "MailingList", "SummaryViewPopup/ReplyTo/List", GTK_UI_MANAGER_MENUITEM) 586 587 /* submenus - mark */ 588 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Mark", "Message/Marks/Mark", GTK_UI_MANAGER_MENUITEM) 589 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Unmark", "Message/Marks/Unmark", GTK_UI_MANAGER_MENUITEM) 590 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator1", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR) 591 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkRead", "Message/Marks/MarkRead", GTK_UI_MANAGER_MENUITEM) 592 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkUnread", "Message/Marks/MarkUnread", GTK_UI_MANAGER_MENUITEM) 593 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator2", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR) 594 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkAllRead", "Message/Marks/MarkAllRead", GTK_UI_MANAGER_MENUITEM) 595 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "MarkAllUnread", "Message/Marks/MarkAllUnread", GTK_UI_MANAGER_MENUITEM) 596 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator3", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR) 597 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "IgnoreThread", "Message/Marks/IgnoreThread", GTK_UI_MANAGER_MENUITEM) 598 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "UnignoreThread", "Message/Marks/UnignoreThread", GTK_UI_MANAGER_MENUITEM) 599 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "WatchThread", "Message/Marks/WatchThread", GTK_UI_MANAGER_MENUITEM) 600 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "UnwatchThread", "Message/Marks/UnwatchThread", GTK_UI_MANAGER_MENUITEM) 601 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Separator4", "Message/Marks/---", GTK_UI_MANAGER_SEPARATOR) 602 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Lock", "Message/Marks/Lock", GTK_UI_MANAGER_MENUITEM) 603 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/Marks", "Unlock", "Message/Marks/Unlock", GTK_UI_MANAGER_MENUITEM) 604 605 /* submenus - view */ 606 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "OpenNewWindow", "View/OpenNewWindow", GTK_UI_MANAGER_MENUITEM) 607 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "MessageSource", "View/MessageSource", GTK_UI_MANAGER_MENUITEM) 608 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewPopup/View", "AllHeaders", "View/AllHeaders", GTK_UI_MANAGER_MENUITEM) 609 610 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "SummaryViewHeaderPopup", "SummaryViewHeaderPopup", GTK_UI_MANAGER_MENU) 611 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "LockColumnHeaders", "SummaryViewHeaderPopup/LockColumnHeaders", GTK_UI_MANAGER_MENUITEM) 612 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup", "SetDisplayedColumns", "SummaryViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM) 613 614 summaryview->popupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM( 615 gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewPopup")) ); 616 summaryview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM( 617 gtk_ui_manager_get_widget(mainwin->ui_manager, "/Menus/SummaryViewHeaderPopup")) ); 618 619 summaryview->vbox = vbox; 620 summaryview->scrolledwin = scrolledwin; 621 summaryview->ctree = ctree; 622 summaryview->hbox = hbox; 623 summaryview->hbox_l = hbox_l; 624 summaryview->hbox_spc = hbox_spc; 625 summaryview->stat_box = stat_box; 626 summaryview->stat_box2 = stat_box2; 627 summaryview->statlabel_folder = statlabel_folder; 628 summaryview->statlabel_select = statlabel_select; 629 summaryview->statlabel_msgs = statlabel_msgs; 630 summaryview->toggle_eventbox = toggle_eventbox; 631 summaryview->toggle_arrow = toggle_arrow; 632 summaryview->lock_count = 0; 633 summaryview->msginfo_update_callback_id = 634 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, summary_update_msg, (gpointer) summaryview); 635 summaryview->folder_item_update_callback_id = 636 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, 637 summary_update_folder_item_hook, 638 (gpointer) summaryview); 639 summaryview->folder_update_callback_id = 640 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, 641 summary_update_folder_hook, 642 (gpointer) summaryview); 643 644 summaryview->target_list = gtk_target_list_new(summary_drag_types, 3); 645 646 /* CLAWS: need this to get the SummaryView * from 647 * the CList */ 648 g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 649 650 gtk_widget_show_all(vbox); 651 652 gtk_widget_show(vbox); 653 return summaryview; 654 } 655 656 void summary_relayout(SummaryView *summaryview) 657 { 658 gtk_widget_realize(summaryview->stat_box); 659 660 g_object_ref(summaryview->hbox_l); 661 g_object_ref(summaryview->statlabel_msgs); 662 663 gtk_container_remove(GTK_CONTAINER( 664 gtk_widget_get_parent(summaryview->hbox_l)), summaryview->hbox_l); 665 gtk_container_remove(GTK_CONTAINER( 666 gtk_widget_get_parent(summaryview->statlabel_msgs)), summaryview->statlabel_msgs); 667 668 switch (prefs_common.layout_mode) { 669 case NORMAL_LAYOUT: 670 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), 671 summaryview->hbox_l, TRUE, TRUE, 0); 672 gtk_box_pack_end(GTK_BOX(summaryview->stat_box), 673 summaryview->statlabel_msgs, FALSE, FALSE, HSPACING_NARROW); 674 gtk_widget_show_all(summaryview->stat_box); 675 gtk_widget_show_all(summaryview->stat_box2); 676 gtk_widget_show(summaryview->toggle_eventbox); 677 break; 678 case VERTICAL_LAYOUT: 679 gtk_box_pack_start(GTK_BOX(summaryview->stat_box), 680 summaryview->hbox_l, TRUE, TRUE, 0); 681 gtk_box_pack_start(GTK_BOX(summaryview->stat_box2), 682 summaryview->statlabel_msgs, FALSE, FALSE, HSPACING_NARROW); 683 gtk_widget_show_all(summaryview->stat_box); 684 gtk_widget_show_all(summaryview->stat_box2); 685 gtk_widget_show(summaryview->toggle_eventbox); 686 gtk_widget_show(summaryview->statlabel_msgs); 687 break; 688 } 689 summary_set_column_order(summaryview); 690 691 g_object_unref(summaryview->hbox_l); 692 g_object_unref(summaryview->statlabel_msgs); 693 } 694 695 static void summary_set_folder_pixmap(SummaryView *summaryview, StockPixmap icon) 696 { 697 GtkWidget *pixmap; 698 if (!summaryview->folder_pixmap_eventbox) { 699 summaryview->folder_pixmap_eventbox = gtk_event_box_new(); 700 gtk_widget_show(summaryview->folder_pixmap_eventbox); 701 gtk_box_pack_start(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, FALSE, FALSE, 4); 702 gtk_box_reorder_child(GTK_BOX(summaryview->hbox_l), summaryview->folder_pixmap_eventbox, 0); /* search_toggle before */ 703 g_signal_connect(G_OBJECT(summaryview->folder_pixmap_eventbox), "button_press_event", 704 G_CALLBACK(summary_folder_eventbox_pressed), 705 summaryview); 706 } 707 if (summaryview->folder_pixmap) 708 gtk_widget_destroy(summaryview->folder_pixmap); 709 710 pixmap = stock_pixmap_widget(icon); 711 gtk_container_add(GTK_CONTAINER(summaryview->folder_pixmap_eventbox), pixmap); 712 gtk_widget_show(pixmap); 713 summaryview->folder_pixmap = pixmap; 714 } 715 716 void summary_init(SummaryView *summaryview) 717 { 718 GtkWidget *pixmap; 719 720 gtk_widget_realize(summaryview->ctree); 721 stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm); 722 stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm); 723 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm); 724 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm); 725 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm); 726 stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm); 727 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm); 728 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm); 729 stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm); 730 stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm); 731 stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm); 732 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm); 733 stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm); 734 stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm); 735 stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm); 736 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm); 737 stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm); 738 stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm); 739 stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm); 740 741 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN); 742 743 /* Init summaryview prefs */ 744 summaryview->sort_key = SORT_BY_NONE; 745 summaryview->sort_type = SORT_ASCENDING; 746 747 /* Init summaryview extra data */ 748 summaryview->simplify_subject_preg = NULL; 749 summary_clear_list(summaryview); 750 summary_set_column_titles(summaryview); 751 main_create_mailing_list_menu (summaryview->mainwin, NULL); 752 summary_set_menu_sensitive(summaryview); 753 754 summaryview->header_menu_lock = FALSE; 755 } 756 757 #define CURRENTLY_DISPLAYED(m) \ 758 ( (m->msgnum == displayed_msgnum) \ 759 && (!g_ascii_strcasecmp(m->folder->name,item->name)) ) 760 761 #define FOLDER_SHOWS_TO_HDR(i) \ 762 ( i && (folder_has_parent_of_type(i, F_OUTBOX) \ 763 || folder_has_parent_of_type(i, F_DRAFT) \ 764 || folder_has_parent_of_type(i, F_QUEUE)) ) 765 766 static void summary_switch_from_to(SummaryView *summaryview, FolderItem *item) 767 { 768 gboolean show_from = FALSE, show_to = FALSE; 769 gboolean showing_from = FALSE, showing_to = FALSE; 770 gint from_pos = 0, to_pos = 0; 771 SummaryColumnState *col_state = summaryview->col_state; 772 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 773 774 if (!item || ((prefs_common.layout_mode == VERTICAL_LAYOUT) && prefs_common.two_line_vert) ) 775 return; 776 if (FOLDER_SHOWS_TO_HDR(item)) 777 show_to = TRUE; 778 else 779 show_from = TRUE; 780 781 from_pos = summaryview->col_pos[S_COL_FROM]; 782 to_pos = summaryview->col_pos[S_COL_TO]; 783 showing_from = col_state[from_pos].visible; 784 showing_to = col_state[to_pos].visible; 785 786 if (showing_from && showing_to) { 787 debug_print("showing both\n"); 788 return; 789 } 790 791 if (!showing_from && !showing_to) { 792 debug_print("showing none\n"); 793 return; 794 } 795 796 debug_print("showing %s %s, must show %s %s\n", 797 showing_from?"From":"", 798 showing_to?"To":"", 799 show_from?"From":"", 800 show_to?"To":""); 801 802 if (showing_from == show_from && showing_to == show_to) 803 return; 804 /* else we'll switch both */ 805 806 debug_print("switching columns\n"); 807 col_state[from_pos].type = S_COL_TO; 808 col_state[from_pos].visible = show_to; 809 810 col_state[to_pos].type = S_COL_FROM; 811 col_state[to_pos].visible = show_from; 812 813 summaryview->col_pos[S_COL_TO] = from_pos; 814 summaryview->col_pos[S_COL_FROM] = to_pos; 815 816 gtk_cmclist_set_column_visibility 817 (GTK_CMCLIST(ctree), from_pos, col_state[from_pos].visible); 818 gtk_cmclist_set_column_visibility 819 (GTK_CMCLIST(ctree), to_pos, col_state[to_pos].visible); 820 821 summary_set_column_titles(summaryview); 822 } 823 824 static gboolean summary_check_consistency(FolderItem *item, GSList *mlist) 825 { 826 int u = 0, n = 0, m = 0, t = 0, r = 0, f = 0, l = 0, i = 0, w = 0; 827 GSList *cur; 828 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) { 829 MsgInfo * msginfo = (MsgInfo *) cur->data; 830 t++; 831 if (MSG_IS_NEW(msginfo->flags)) 832 n++; 833 if (MSG_IS_UNREAD(msginfo->flags)) 834 u++; 835 if (MSG_IS_MARKED(msginfo->flags)) 836 m++; 837 if (MSG_IS_REPLIED(msginfo->flags)) 838 r++; 839 if (MSG_IS_FORWARDED(msginfo->flags)) 840 f++; 841 if (MSG_IS_LOCKED(msginfo->flags)) 842 l++; 843 if (MSG_IS_IGNORE_THREAD(msginfo->flags)) 844 i++; 845 if (MSG_IS_WATCH_THREAD(msginfo->flags)) 846 w++; 847 } 848 if (t != item->total_msgs 849 || n != item->new_msgs 850 || u != item->unread_msgs 851 || m != item->marked_msgs 852 || r != item->replied_msgs 853 || f != item->forwarded_msgs 854 || l != item->locked_msgs 855 || i != item->ignored_msgs 856 || w != item->watched_msgs 857 || (m == 0 && item->unreadmarked_msgs != 0) 858 || item->unreadmarked_msgs < 0) { 859 debug_print("Inconsistency\n"); 860 folder_item_scan_full(item, FALSE); 861 return FALSE; 862 } 863 return TRUE; 864 } 865 866 gboolean summaryview_search_root_progress(gpointer data, guint at, guint matched, guint total) 867 { 868 gint interval = 100; 869 870 statusbar_progress_all(at, total, interval); 871 872 if (at % interval == 0) 873 GTK_EVENTS_FLUSH(); 874 875 return TRUE; 876 } 877 878 gboolean summary_show(SummaryView *summaryview, FolderItem *item, gboolean avoid_refresh) 879 { 880 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 881 GtkCMCTreeNode *node = NULL; 882 GSList *mlist = NULL; 883 gchar *buf; 884 gboolean is_refresh; 885 guint selected_msgnum = 0; 886 guint displayed_msgnum = 0; 887 GSList *cur; 888 GSList *not_killed; 889 gboolean hidden_removed = FALSE; 890 891 if (summary_is_locked(summaryview)) return FALSE; 892 893 if (!summaryview->mainwin) 894 return FALSE; 895 summary_switch_from_to(summaryview, item); 896 897 inc_lock(); 898 summary_lock(summaryview); 899 900 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE); 901 902 utils_free_regex(); 903 904 is_refresh = (item == summaryview->folder_item && !avoid_refresh) ? TRUE : FALSE; 905 906 if (item && item->folder->klass->item_opened) { 907 item->folder->klass->item_opened(item); 908 } 909 910 if (!is_refresh) { 911 main_create_mailing_list_menu (summaryview->mainwin, NULL); 912 } else { 913 selected_msgnum = summary_get_msgnum(summaryview, 914 summaryview->selected); 915 displayed_msgnum = summary_get_msgnum(summaryview, 916 summaryview->displayed); 917 } 918 919 /* process the marks if any */ 920 if (!is_refresh && 921 (summaryview->mainwin->lock_count == 0 && 922 (summaryview->moved > 0 || summaryview->copied > 0))) { 923 AlertValue val; 924 gboolean changed = FALSE; 925 926 val = alertpanel(_("Process mark"), 927 _("Some marks are left. Process them?"), 928 NULL, _("_No"), NULL, _("_Yes"), NULL, _("_Cancel"), 929 ALERTFOCUS_FIRST); 930 if (G_ALERTALTERNATE == val) { 931 summary_unlock(summaryview); 932 summary_execute(summaryview); 933 summary_lock(summaryview); 934 changed = TRUE; 935 } else if (G_ALERTDEFAULT == val) { 936 /* DO NOTHING */ 937 } else { 938 summary_unlock(summaryview); 939 inc_unlock(); 940 return FALSE; 941 } 942 if (changed) 943 folder_update_op_count(); 944 } 945 946 summary_freeze(summaryview); 947 948 summary_clear_list(summaryview); 949 950 buf = NULL; 951 if (!item || !item->path || !folder_item_parent(item) || item->no_select) { 952 g_free(buf); 953 debug_print("empty folder (%p %s %p %d)\n", 954 item, 955 (item && item->path)?item->path:"(null)", 956 item?folder_item_parent(item):0x0, 957 item?item->no_select:FALSE); 958 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE); 959 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE); 960 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE); 961 summary_clear_all(summaryview); 962 summaryview->folder_item = item; 963 summary_thaw(summaryview); 964 summary_unlock(summaryview); 965 inc_unlock(); 966 return TRUE; 967 } 968 g_free(buf); 969 970 if (!is_refresh) 971 messageview_clear(summaryview->messageview); 972 973 summaryview->folder_item = item; 974 item->opened = TRUE; 975 976 buf = g_strdup_printf(_("Scanning folder (%s)..."), item->path); 977 debug_print("%s\n", buf); 978 STATUSBAR_PUSH(summaryview->mainwin, buf); 979 g_free(buf); 980 981 main_window_cursor_wait(summaryview->mainwin); 982 983 mlist = folder_item_get_msg_list(item); 984 985 if (!summary_check_consistency(item, mlist)) { 986 debug_print("reloading due to inconsistency\n"); 987 procmsg_msg_list_free(mlist); 988 mlist = folder_item_get_msg_list(item); 989 } 990 991 if ((summaryview->folder_item->hide_read_msgs 992 || summaryview->folder_item->hide_del_msgs 993 || summaryview->folder_item->hide_read_threads)) { 994 GSList *not_killed; 995 996 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", 997 summaryview->folder_item->hide_read_msgs); 998 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", 999 summaryview->folder_item->hide_del_msgs); 1000 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", 1001 summaryview->folder_item->hide_read_threads); 1002 not_killed = NULL; 1003 for(cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) { 1004 MsgInfo * msginfo = (MsgInfo *) cur->data; 1005 1006 if (!msginfo->hidden) { 1007 if (MSG_IS_DELETED(msginfo->flags) && summaryview->folder_item->hide_del_msgs) { 1008 procmsg_msginfo_free(&msginfo); 1009 continue; 1010 } 1011 if (summaryview->folder_item->hide_read_msgs) { 1012 if (MSG_IS_UNREAD(msginfo->flags) && 1013 !MSG_IS_IGNORE_THREAD(msginfo->flags)) 1014 not_killed = g_slist_prepend(not_killed, msginfo); 1015 else if (MSG_IS_MARKED(msginfo->flags) || 1016 MSG_IS_LOCKED(msginfo->flags)) 1017 not_killed = g_slist_prepend(not_killed, msginfo); 1018 else if (is_refresh && 1019 (msginfo->msgnum == selected_msgnum || 1020 msginfo->msgnum == displayed_msgnum)) 1021 not_killed = g_slist_prepend(not_killed, msginfo); 1022 else 1023 procmsg_msginfo_free(&msginfo); 1024 } else { 1025 not_killed = g_slist_prepend(not_killed, msginfo); 1026 } 1027 } else 1028 procmsg_msginfo_free(&msginfo); 1029 } 1030 hidden_removed = TRUE; 1031 g_slist_free(mlist); 1032 mlist = not_killed; 1033 } else { 1034 summary_set_hide_menu(summaryview, "/Menu/View/HideReadMessages", FALSE); 1035 summary_set_hide_menu(summaryview, "/Menu/View/HideDelMessages", FALSE); 1036 summary_set_hide_menu(summaryview, "/Menu/View/HideReadThreads", FALSE); 1037 } 1038 1039 if (!hidden_removed) { 1040 not_killed = NULL; 1041 for (cur = mlist ; cur != NULL && cur->data != NULL ; cur = g_slist_next(cur)) { 1042 MsgInfo * msginfo = (MsgInfo *) cur->data; 1043 if (!msginfo->hidden) 1044 not_killed = g_slist_prepend(not_killed, msginfo); 1045 else 1046 procmsg_msginfo_free(&msginfo); 1047 } 1048 g_slist_free(mlist); 1049 mlist = not_killed; 1050 } 1051 1052 STATUSBAR_POP(summaryview->mainwin); 1053 1054 /* set ctree and hash table from the msginfo list, and 1055 create the thread */ 1056 summary_set_ctree_from_list(summaryview, mlist, selected_msgnum); 1057 1058 g_slist_free(mlist); 1059 1060 if (is_refresh) { 1061 summaryview->displayed = 1062 summary_find_msg_by_msgnum(summaryview, 1063 displayed_msgnum); 1064 if (!summaryview->displayed) 1065 messageview_clear(summaryview->messageview); 1066 summary_unlock(summaryview); 1067 1068 summary_select_by_msgnum(summaryview, selected_msgnum, FALSE); 1069 1070 summary_lock(summaryview); 1071 if (!summaryview->selected) { 1072 /* no selected message - select first unread 1073 message, but do not display it */ 1074 node = summary_find_next_flagged_msg(summaryview, NULL, 1075 MSG_UNREAD, FALSE); 1076 if (node == NULL && GTK_CMCLIST(ctree)->row_list != NULL) 1077 node = gtk_cmctree_node_nth 1078 (ctree, 1079 item->sort_type == SORT_DESCENDING 1080 ? 0 : GTK_CMCLIST(ctree)->rows - 1); 1081 summary_unlock(summaryview); 1082 1083 summary_select_node(summaryview, node, OPEN_SELECTED_ON_FOLDER_OPEN); 1084 1085 summary_lock(summaryview); 1086 } 1087 } else { 1088 /* backward compat */ 1089 int i = 0; 1090 gboolean set = FALSE, stop = FALSE; 1091 for (i = 0; i < 6; i++) { 1092 EntryAction act = prefs_common.summary_select_prio[i]; 1093 1094 if (act != ACTION_UNSET) { 1095 set = TRUE; 1096 break; 1097 } 1098 } 1099 if (!set) 1100 prefs_summary_open_set_defaults(); 1101 1102 for (i = 0; i < 6 && node == NULL; i++) { 1103 EntryAction act = prefs_common.summary_select_prio[i]; 1104 1105 switch(act) { 1106 case ACTION_OLDEST_MARKED: 1107 if (summaryview->sort_type == SORT_ASCENDING) 1108 node = summary_find_next_flagged_msg(summaryview, NULL, 1109 MSG_MARKED, FALSE); 1110 else 1111 node = summary_find_prev_flagged_msg(summaryview, NULL, 1112 MSG_MARKED, FALSE); 1113 break; 1114 case ACTION_NEWEST_MARKED: 1115 if (summaryview->sort_type == SORT_ASCENDING) 1116 node = summary_find_prev_flagged_msg(summaryview, NULL, 1117 MSG_MARKED, FALSE); 1118 else 1119 node = summary_find_next_flagged_msg(summaryview, NULL, 1120 MSG_MARKED, FALSE); 1121 break; 1122 case ACTION_OLDEST_NEW: 1123 if (summaryview->sort_type == SORT_ASCENDING) 1124 node = summary_find_next_flagged_msg(summaryview, NULL, 1125 MSG_NEW, FALSE); 1126 else 1127 node = summary_find_prev_flagged_msg(summaryview, NULL, 1128 MSG_NEW, FALSE); 1129 break; 1130 case ACTION_NEWEST_NEW: 1131 if (summaryview->sort_type == SORT_ASCENDING) 1132 node = summary_find_prev_flagged_msg(summaryview, NULL, 1133 MSG_NEW, FALSE); 1134 else 1135 node = summary_find_next_flagged_msg(summaryview, NULL, 1136 MSG_NEW, FALSE); 1137 break; 1138 case ACTION_OLDEST_UNREAD: 1139 if (summaryview->sort_type == SORT_ASCENDING) 1140 node = summary_find_next_flagged_msg(summaryview, NULL, 1141 MSG_UNREAD, FALSE); 1142 else 1143 node = summary_find_prev_flagged_msg(summaryview, NULL, 1144 MSG_UNREAD, FALSE); 1145 break; 1146 case ACTION_NEWEST_UNREAD: 1147 if (summaryview->sort_type == SORT_ASCENDING) 1148 node = summary_find_prev_flagged_msg(summaryview, NULL, 1149 MSG_UNREAD, FALSE); 1150 else 1151 node = summary_find_next_flagged_msg(summaryview, NULL, 1152 MSG_UNREAD, FALSE); 1153 break; 1154 case ACTION_LAST_OPENED: 1155 if (summaryview->folder_item) { 1156 node = summary_find_msg_by_msgnum(summaryview, 1157 summaryview->folder_item->last_seen); 1158 } 1159 break; 1160 case ACTION_NEWEST_LIST: 1161 if (GTK_CMCLIST(ctree)->row_list != NULL) { 1162 node = gtk_cmctree_node_nth 1163 (ctree, 1164 item->sort_type == SORT_DESCENDING 1165 ? 0 : GTK_CMCLIST(ctree)->rows - 1); 1166 } 1167 break; 1168 case ACTION_OLDEST_LIST: 1169 if (GTK_CMCLIST(ctree)->row_list != NULL) { 1170 node = gtk_cmctree_node_nth 1171 (ctree, 1172 item->sort_type == SORT_ASCENDING 1173 ? 0 : GTK_CMCLIST(ctree)->rows - 1); 1174 } 1175 break; 1176 case ACTION_NOTHING: 1177 case ACTION_UNSET: 1178 node = NULL; 1179 stop = TRUE; 1180 break; 1181 } 1182 1183 if (stop || node) 1184 break; 1185 } 1186 1187 summary_unlock(summaryview); 1188 1189 if (node) 1190 summary_select_node(summaryview, node, 1191 OPEN_SELECTED_ON_FOLDER_OPEN); 1192 1193 summary_lock(summaryview); 1194 } 1195 1196 summary_status_show(summaryview); 1197 summary_set_menu_sensitive(summaryview); 1198 toolbar_main_set_sensitive(summaryview->mainwin); 1199 1200 summary_thaw(summaryview); 1201 STATUSBAR_PUSH(summaryview->mainwin, _("Done.")); 1202 STATUSBAR_POP(summaryview->mainwin); 1203 main_window_cursor_normal(summaryview->mainwin); 1204 summary_unlock(summaryview); 1205 inc_unlock(); 1206 return TRUE; 1207 } 1208 1209 #undef CURRENTLY_DISPLAYED 1210 1211 static void summary_cancel_mark_read_timeout(SummaryView *summaryview) { 1212 if (summaryview->mark_as_read_timeout_tag != 0) { 1213 g_source_remove(summaryview->mark_as_read_timeout_tag); 1214 summaryview->mark_as_read_timeout_tag = 0; 1215 } 1216 } 1217 1218 void summary_clear_list(SummaryView *summaryview) 1219 { 1220 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 1221 gint optimal_width; 1222 1223 summary_freeze(summaryview); 1224 1225 gtk_cmctree_pre_recursive(GTK_CMCTREE(summaryview->ctree), 1226 NULL, summary_free_msginfo_func, NULL); 1227 1228 if (summaryview->folder_item) { 1229 summaryview->folder_item->opened = FALSE; 1230 summaryview->folder_item = NULL; 1231 } 1232 1233 summary_cancel_mark_read_timeout(summaryview); 1234 1235 summaryview->display_msg = FALSE; 1236 1237 summaryview->selected = NULL; 1238 summaryview->displayed = NULL; 1239 summaryview->total_size = 0; 1240 summaryview->deleted = summaryview->moved = 0; 1241 summaryview->copied = 0; 1242 if (summaryview->msgid_table) { 1243 g_hash_table_destroy(summaryview->msgid_table); 1244 summaryview->msgid_table = NULL; 1245 } 1246 if (summaryview->subject_table) { 1247 g_hash_table_destroy(summaryview->subject_table); 1248 summaryview->subject_table = NULL; 1249 } 1250 summaryview->mlist = NULL; 1251 1252 gtk_cmclist_clear(clist); 1253 if (summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) { 1254 optimal_width = gtk_cmclist_optimal_column_width 1255 (clist, summaryview->col_pos[S_COL_SUBJECT]); 1256 gtk_cmclist_set_column_width 1257 (clist, summaryview->col_pos[S_COL_SUBJECT], 1258 optimal_width); 1259 } 1260 1261 summary_thaw(summaryview); 1262 } 1263 1264 void summary_clear_all(SummaryView *summaryview) 1265 { 1266 mimeview_clear(summaryview->messageview->mimeview); 1267 messageview_clear(summaryview->messageview); 1268 summary_clear_list(summaryview); 1269 summary_set_menu_sensitive(summaryview); 1270 toolbar_main_set_sensitive(summaryview->mainwin); 1271 summary_status_show(summaryview); 1272 } 1273 1274 void summary_lock(SummaryView *summaryview) 1275 { 1276 summaryview->lock_count++; 1277 } 1278 1279 void summary_unlock(SummaryView *summaryview) 1280 { 1281 if (summaryview->lock_count) 1282 summaryview->lock_count--; 1283 } 1284 1285 gboolean summary_is_locked(SummaryView *summaryview) 1286 { 1287 return summaryview->lock_count > 0; 1288 } 1289 1290 SummarySelection summary_get_selection_type(SummaryView *summaryview) 1291 { 1292 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 1293 SummarySelection selection; 1294 1295 if (!clist->row_list) 1296 selection = SUMMARY_NONE; 1297 else if (!clist->selection) 1298 selection = SUMMARY_SELECTED_NONE; 1299 else if (!clist->selection->next) 1300 selection = SUMMARY_SELECTED_SINGLE; 1301 else 1302 selection = SUMMARY_SELECTED_MULTIPLE; 1303 1304 return selection; 1305 } 1306 1307 /*! 1308 *\return MsgInfo * Selected message if there's one selected; 1309 * if multiple selected, or none, return NULL. 1310 */ 1311 MsgInfo *summary_get_selected_msg(SummaryView *summaryview) 1312 { 1313 /* summaryview->selected may be valid when multiple 1314 * messages were selected */ 1315 GList *sellist = GTK_CMCLIST(summaryview->ctree)->selection; 1316 1317 if (sellist == NULL || sellist->next) 1318 return NULL; 1319 1320 return GTKUT_CTREE_NODE_GET_ROW_DATA(sellist->data); 1321 } 1322 1323 GSList *summary_get_selected_msg_list(SummaryView *summaryview) 1324 { 1325 GSList *mlist = NULL; 1326 GList *cur; 1327 MsgInfo *msginfo; 1328 1329 for (cur = GTK_CMCLIST(summaryview->ctree)->selection; cur != NULL && cur->data != NULL; 1330 cur = cur->next) { 1331 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(cur->data); 1332 mlist = g_slist_prepend(mlist, 1333 procmsg_msginfo_new_ref(msginfo)); 1334 } 1335 1336 mlist = g_slist_reverse(mlist); 1337 1338 return mlist; 1339 } 1340 1341 void summary_set_menu_sensitive(SummaryView *summaryview) 1342 { 1343 SensitiveCondMask state; 1344 1345 main_window_set_menu_sensitive(summaryview->mainwin); 1346 1347 state = main_window_get_current_state(summaryview->mainwin); 1348 1349 #define SET_SENSITIVE(entry_str, ...) \ 1350 { \ 1351 SensitiveCondMask cond = main_window_get_mask(__VA_ARGS__, -1); \ 1352 cm_menu_set_sensitive_full(summaryview->mainwin->ui_manager, \ 1353 (const gchar *) entry_str, \ 1354 ((cond & state) == cond)); \ 1355 } 1356 SET_SENSITIVE("Menus/SummaryViewPopup/Reedit", M_ALLOW_REEDIT); 1357 SET_SENSITIVE("Menus/SummaryViewPopup/Reply", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1358 1359 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1360 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/All", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1361 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/Sender", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1362 SET_SENSITIVE("Menus/SummaryViewPopup/ReplyTo/MailingList", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1363 1364 SET_SENSITIVE("Menus/SummaryViewPopup/Forward", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1365 1366 SET_SENSITIVE("Menus/SummaryViewPopup/ForwardAtt", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1367 SET_SENSITIVE("Menus/SummaryViewPopup/Redirect", M_HAVE_ACCOUNT, M_TARGET_EXIST); 1368 1369 SET_SENSITIVE("Menus/SummaryViewPopup/Move", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS); 1370 SET_SENSITIVE("Menus/SummaryViewPopup/Copy", M_TARGET_EXIST, M_EXEC); 1371 SET_SENSITIVE("Menus/SummaryViewPopup/Trash", M_TARGET_EXIST, M_ALLOW_DELETE, M_NOT_NEWS, M_NOT_TRASH); 1372 SET_SENSITIVE("Menus/SummaryViewPopup/Delete", M_TARGET_EXIST, M_ALLOW_DELETE); 1373 1374 SET_SENSITIVE("Menus/SummaryViewPopup/Marks", M_TARGET_EXIST); 1375 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Mark", M_TARGET_EXIST); 1376 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Unmark", M_TARGET_EXIST); 1377 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkRead", M_TARGET_EXIST); 1378 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkUnread", M_TARGET_EXIST); 1379 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkAllRead", M_TARGET_EXIST); 1380 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/MarkAllUnread", M_TARGET_EXIST); 1381 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/IgnoreThread", M_TARGET_EXIST); 1382 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/UnignoreThread", M_TARGET_EXIST); 1383 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/WatchThread", M_TARGET_EXIST); 1384 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/UnwatchThread", M_TARGET_EXIST); 1385 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Lock", M_TARGET_EXIST); 1386 SET_SENSITIVE("Menus/SummaryViewPopup/Marks/Unlock", M_TARGET_EXIST); 1387 1388 SET_SENSITIVE("Menus/SummaryViewPopup/View", M_SINGLE_TARGET_EXIST); 1389 SET_SENSITIVE("Menus/SummaryViewPopup/View/OpenNewWindow", M_SINGLE_TARGET_EXIST); 1390 SET_SENSITIVE("Menus/SummaryViewPopup/View/MessageSource", M_SINGLE_TARGET_EXIST); 1391 SET_SENSITIVE("Menus/SummaryViewPopup/View/AllHeaders", M_SINGLE_TARGET_EXIST); 1392 SET_SENSITIVE("Menus/SummaryViewPopup/SaveAs", M_TARGET_EXIST); 1393 #undef SET_SENSITIVE 1394 1395 summary_lock(summaryview); 1396 if (summaryview->messageview 1397 && summaryview->messageview->mimeview 1398 && summaryview->messageview->mimeview->textview) 1399 cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, "Menus/SummaryViewPopup/View/AllHeaders", 1400 prefs_common.show_all_headers); 1401 summary_unlock(summaryview); 1402 } 1403 1404 void summary_select_prev(SummaryView *summaryview) 1405 { 1406 GtkCMCTreeNode *node = summaryview->selected; 1407 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1408 1409 if (summaryview->sort_type == SORT_ASCENDING) 1410 node = gtkut_ctree_node_prev(ctree, node); 1411 else 1412 node = gtkut_ctree_node_next(ctree, node); 1413 1414 if (node && node != summaryview->selected) 1415 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1416 } 1417 1418 void summary_select_next(SummaryView *summaryview) 1419 { 1420 GtkCMCTreeNode *node = summaryview->selected; 1421 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1422 1423 if (summaryview->sort_type == SORT_ASCENDING) 1424 node = gtkut_ctree_node_next(ctree, node); 1425 else 1426 node = gtkut_ctree_node_prev(ctree, node); 1427 1428 if (node && node != summaryview->selected) 1429 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1430 } 1431 1432 void summary_select_prev_unread(SummaryView *summaryview) 1433 { 1434 GtkCMCTreeNode *node; 1435 1436 if (summaryview->sort_type == SORT_ASCENDING) 1437 node = summary_find_prev_flagged_msg 1438 (summaryview, summaryview->selected, MSG_UNREAD, TRUE); 1439 else 1440 node = summary_find_next_flagged_msg 1441 (summaryview, summaryview->selected, MSG_UNREAD, TRUE); 1442 1443 if (!node || node == summaryview->selected) { 1444 AlertValue val = 0; 1445 1446 switch (prefs_common.next_unread_msg_dialog) { 1447 case NEXTUNREADMSGDIALOG_ALWAYS: 1448 val = alertpanel(_("No more unread messages"), 1449 _("No unread message found. " 1450 "Search from the end?"), 1451 NULL, _("_No"), NULL, _("_Yes"), 1452 NULL, NULL, ALERTFOCUS_SECOND); 1453 break; 1454 case NEXTUNREADMSGDIALOG_ASSUME_YES: 1455 val = G_ALERTALTERNATE; 1456 break; 1457 case NEXTUNREADMSGDIALOG_ASSUME_NO: 1458 val = !G_ALERTALTERNATE; 1459 break; 1460 default: 1461 debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"); 1462 } 1463 if (val != G_ALERTALTERNATE) return; 1464 if (summaryview->sort_type == SORT_ASCENDING) 1465 node = summary_find_prev_flagged_msg(summaryview, NULL, 1466 MSG_UNREAD, FALSE); 1467 else 1468 node = summary_find_next_flagged_msg(summaryview, NULL, 1469 MSG_UNREAD, FALSE); 1470 } 1471 1472 if (!node) 1473 alertpanel_notice(_("No unread messages.")); 1474 else 1475 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1476 } 1477 1478 void summary_select_next_unread(SummaryView *summaryview) 1479 { 1480 GtkCMCTreeNode *node = summaryview->selected; 1481 1482 if (summaryview->sort_type == SORT_ASCENDING) 1483 node = summary_find_next_flagged_msg 1484 (summaryview, node, MSG_UNREAD, TRUE); 1485 else 1486 node = summary_find_prev_flagged_msg 1487 (summaryview, node, MSG_UNREAD, TRUE); 1488 1489 if (node) 1490 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1491 else { 1492 AlertValue val = 0; 1493 1494 switch (prefs_common.next_unread_msg_dialog) { 1495 case NEXTUNREADMSGDIALOG_ALWAYS: 1496 val = alertpanel(_("No more unread messages"), 1497 _("No unread message found. " 1498 "Go to next folder?"), 1499 NULL, _("_No"), NULL, _("_Yes"), 1500 NULL, NULL, ALERTFOCUS_SECOND); 1501 break; 1502 case NEXTUNREADMSGDIALOG_ASSUME_YES: 1503 val = G_ALERTALTERNATE; 1504 break; 1505 case NEXTUNREADMSGDIALOG_ASSUME_NO: 1506 val = G_ALERTOTHER; 1507 break; 1508 default: 1509 debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"); 1510 } 1511 1512 if (val == G_ALERTALTERNATE) 1513 folderview_select_next_with_flag(summaryview->folderview, MSG_UNREAD); 1514 } 1515 } 1516 1517 void summary_select_prev_new(SummaryView *summaryview) 1518 { 1519 GtkCMCTreeNode *node; 1520 1521 if (summaryview->sort_type == SORT_ASCENDING) 1522 node = summary_find_prev_flagged_msg 1523 (summaryview, summaryview->selected, MSG_NEW, TRUE); 1524 else 1525 node = summary_find_next_flagged_msg 1526 (summaryview, summaryview->selected, MSG_NEW, TRUE); 1527 1528 if (!node || node == summaryview->selected) { 1529 AlertValue val = 0; 1530 1531 switch (prefs_common.next_unread_msg_dialog) { 1532 case NEXTUNREADMSGDIALOG_ALWAYS: 1533 val = alertpanel(_("No more new messages"), 1534 _("No new message found. " 1535 "Search from the end?"), 1536 NULL, _("_No"), NULL, _("_Yes"), 1537 NULL, NULL, ALERTFOCUS_SECOND); 1538 break; 1539 case NEXTUNREADMSGDIALOG_ASSUME_YES: 1540 val = G_ALERTALTERNATE; 1541 break; 1542 case NEXTUNREADMSGDIALOG_ASSUME_NO: 1543 val = !G_ALERTALTERNATE; 1544 break; 1545 default: 1546 debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"); 1547 } 1548 if (val != G_ALERTALTERNATE) return; 1549 if (summaryview->sort_type == SORT_ASCENDING) 1550 node = summary_find_prev_flagged_msg(summaryview, NULL, 1551 MSG_NEW, FALSE); 1552 else 1553 node = summary_find_next_flagged_msg(summaryview, NULL, 1554 MSG_NEW, FALSE); 1555 } 1556 1557 if (!node) 1558 alertpanel_notice(_("No new messages.")); 1559 else 1560 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1561 } 1562 1563 void summary_select_next_new(SummaryView *summaryview) 1564 { 1565 GtkCMCTreeNode *node = summaryview->selected; 1566 1567 if (summaryview->sort_type == SORT_ASCENDING) 1568 node = summary_find_next_flagged_msg 1569 (summaryview, node, MSG_NEW, TRUE); 1570 else 1571 node = summary_find_prev_flagged_msg 1572 (summaryview, node, MSG_NEW, TRUE); 1573 1574 if (node) 1575 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1576 else { 1577 AlertValue val = 0; 1578 1579 switch (prefs_common.next_unread_msg_dialog) { 1580 case NEXTUNREADMSGDIALOG_ALWAYS: 1581 val = alertpanel(_("No more new messages"), 1582 _("No new message found. " 1583 "Go to next folder?"), 1584 NULL, _("_No"), NULL, _("_Yes"), 1585 NULL, NULL, ALERTFOCUS_SECOND); 1586 break; 1587 case NEXTUNREADMSGDIALOG_ASSUME_YES: 1588 val = G_ALERTALTERNATE; 1589 break; 1590 case NEXTUNREADMSGDIALOG_ASSUME_NO: 1591 val = G_ALERTOTHER; 1592 break; 1593 default: 1594 debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"); 1595 } 1596 if (val == G_ALERTALTERNATE) 1597 folderview_select_next_with_flag(summaryview->folderview, MSG_NEW); 1598 } 1599 } 1600 1601 void summary_select_prev_marked(SummaryView *summaryview) 1602 { 1603 GtkCMCTreeNode *node; 1604 1605 if (summaryview->sort_type == SORT_ASCENDING) 1606 node = summary_find_prev_flagged_msg 1607 (summaryview, summaryview->selected, MSG_MARKED, TRUE); 1608 else 1609 node = summary_find_next_flagged_msg 1610 (summaryview, summaryview->selected, MSG_MARKED, TRUE); 1611 1612 if (!node) { 1613 AlertValue val; 1614 1615 val = alertpanel(_("No more marked messages"), 1616 _("No marked message found. " 1617 "Search from the end?"), 1618 NULL, _("_No"), NULL, _("_Yes"), 1619 NULL, NULL, ALERTFOCUS_SECOND); 1620 if (val != G_ALERTALTERNATE) return; 1621 node = summary_find_prev_flagged_msg(summaryview, NULL, 1622 MSG_MARKED, TRUE); 1623 } 1624 1625 if (!node) 1626 alertpanel_notice(_("No marked messages.")); 1627 else 1628 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1629 } 1630 1631 void summary_select_next_marked(SummaryView *summaryview) 1632 { 1633 GtkCMCTreeNode *node = summaryview->selected; 1634 1635 if (summaryview->sort_type == SORT_ASCENDING) 1636 node = summary_find_next_flagged_msg 1637 (summaryview, node, MSG_MARKED, TRUE); 1638 else 1639 node = summary_find_prev_flagged_msg 1640 (summaryview, node, MSG_MARKED, TRUE); 1641 1642 if (node) 1643 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1644 else { 1645 AlertValue val = 0; 1646 1647 switch (prefs_common.next_unread_msg_dialog) { 1648 case NEXTUNREADMSGDIALOG_ALWAYS: 1649 val = alertpanel(_("No more marked messages"), 1650 _("No marked message found. " 1651 "Go to next folder?"), 1652 NULL, _("_No"), NULL, _("_Yes"), 1653 NULL, NULL, ALERTFOCUS_SECOND); 1654 break; 1655 case NEXTUNREADMSGDIALOG_ASSUME_YES: 1656 val = G_ALERTALTERNATE; 1657 break; 1658 case NEXTUNREADMSGDIALOG_ASSUME_NO: 1659 val = G_ALERTOTHER; 1660 break; 1661 default: 1662 debug_print("Internal error: unexpected value for prefs_common.next_unread_msg_dialog\n"); 1663 } 1664 if (val == G_ALERTALTERNATE) 1665 folderview_select_next_with_flag(summaryview->folderview, MSG_MARKED); 1666 } 1667 } 1668 1669 void summary_select_prev_labeled(SummaryView *summaryview) 1670 { 1671 GtkCMCTreeNode *node; 1672 1673 if (summaryview->sort_type == SORT_ASCENDING) 1674 node = summary_find_prev_flagged_msg 1675 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE); 1676 else 1677 node = summary_find_next_flagged_msg 1678 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE); 1679 1680 if (!node) { 1681 AlertValue val; 1682 1683 val = alertpanel(_("No more labeled messages"), 1684 _("No labeled message found. " 1685 "Search from the end?"), 1686 NULL, _("_No"), NULL, _("_Yes"), 1687 NULL, NULL, ALERTFOCUS_SECOND); 1688 if (val != G_ALERTALTERNATE) return; 1689 node = summary_find_prev_flagged_msg(summaryview, NULL, 1690 MSG_CLABEL_FLAG_MASK, TRUE); 1691 } 1692 1693 if (!node) 1694 alertpanel_notice(_("No labeled messages.")); 1695 else 1696 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1697 } 1698 1699 void summary_select_next_labeled(SummaryView *summaryview) 1700 { 1701 GtkCMCTreeNode *node; 1702 1703 if (summaryview->sort_type == SORT_ASCENDING) 1704 node = summary_find_next_flagged_msg 1705 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE); 1706 else 1707 node = summary_find_prev_flagged_msg 1708 (summaryview, summaryview->selected, MSG_CLABEL_FLAG_MASK, TRUE); 1709 1710 if (!node) { 1711 AlertValue val; 1712 1713 val = alertpanel(_("No more labeled messages"), 1714 _("No labeled message found. " 1715 "Search from the beginning?"), 1716 NULL, _("_No"), NULL, _("_Yes"), 1717 NULL, NULL, ALERTFOCUS_SECOND); 1718 if (val != G_ALERTALTERNATE) return; 1719 if (summaryview->sort_type == SORT_ASCENDING) 1720 node = summary_find_next_flagged_msg(summaryview, NULL, 1721 MSG_CLABEL_FLAG_MASK, TRUE); 1722 else 1723 node = summary_find_prev_flagged_msg(summaryview, NULL, 1724 MSG_CLABEL_FLAG_MASK, TRUE); 1725 } 1726 1727 if (!node) 1728 alertpanel_notice(_("No labeled messages.")); 1729 else 1730 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1731 } 1732 1733 void summary_select_parent(SummaryView *summaryview) 1734 { 1735 GtkCMCTreeNode *node = NULL; 1736 1737 if (summaryview->selected) 1738 node = GTK_CMCTREE_ROW(summaryview->selected)->parent; 1739 if (node) 1740 summary_select_node(summaryview, node, OPEN_SELECTED_ON_PREVNEXT); 1741 } 1742 1743 void summary_select_by_msgnum(SummaryView *summaryview, guint msgnum, 1744 gboolean show) 1745 { 1746 GtkCMCTreeNode *node; 1747 1748 node = summary_find_msg_by_msgnum(summaryview, msgnum); 1749 summary_select_node(summaryview, node, show); 1750 } 1751 1752 void summary_select_by_msg_list(SummaryView *summaryview, GSList *msginfos) 1753 { 1754 GtkCMCTree *ctree; 1755 GSList *msgnum_list, *walk; 1756 gboolean froze = FALSE; 1757 1758 ctree = GTK_CMCTREE(summaryview->ctree); 1759 1760 msgnum_list = procmsg_get_number_list_for_msgs(msginfos); 1761 1762 START_LONG_OPERATION(summaryview, FALSE); 1763 for(walk = msgnum_list; walk; walk = walk->next) { 1764 GtkCMCTreeNode *node; 1765 node = summary_find_msg_by_msgnum(summaryview, GPOINTER_TO_UINT(walk->data)); 1766 if (node != NULL) 1767 gtk_cmctree_select(ctree, node); 1768 } 1769 END_LONG_OPERATION(summaryview); 1770 g_slist_free(msgnum_list); 1771 } 1772 1773 typedef struct _PostponedSelectData 1774 { 1775 GtkCMCTree *ctree; 1776 GtkCMCTreeNode *row; 1777 GtkCMCTreeNode *node; 1778 GtkScrollType type; 1779 gint column; 1780 SummaryView *summaryview; 1781 gboolean display_msg; 1782 } PostponedSelectData; 1783 1784 static gboolean summary_select_retry(void *data) 1785 { 1786 PostponedSelectData *psdata = (PostponedSelectData *)data; 1787 debug_print("trying again\n"); 1788 if (psdata->row) 1789 summary_selected(psdata->ctree, psdata->row, 1790 psdata->column, psdata->summaryview); 1791 else if (psdata->node) 1792 summary_select_node(psdata->summaryview, psdata->node, 1793 psdata->display_msg); 1794 g_free(psdata); 1795 return FALSE; 1796 } 1797 1798 /** 1799 * summary_select_node: 1800 * @summaryview: Summary view. 1801 * @node: Summary tree node. 1802 * @display_msg: whether to also display the message 1803 * 1804 * Select @node (bringing it into view by scrolling and expanding its 1805 * thread, if necessary) and unselect all others. 1806 **/ 1807 1808 void summary_select_node(SummaryView *summaryview, GtkCMCTreeNode *node, 1809 gboolean display_msg) 1810 { 1811 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1812 1813 /* If msgview is hidden, we never want to automatically display 1814 * a selected message, since that would unhide the msgview. */ 1815 if (!messageview_is_visible(summaryview->messageview)) 1816 display_msg = FALSE; 1817 1818 if (summary_is_locked(summaryview) 1819 && !GTK_SCTREE(ctree)->selecting_range 1820 && summaryview->messageview->mimeview 1821 && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT 1822 && summaryview->messageview->mimeview->textview->loading) { 1823 PostponedSelectData *data = g_new0(PostponedSelectData, 1); 1824 summaryview->messageview->mimeview->textview->stop_loading = TRUE; 1825 1826 data->ctree = ctree; 1827 data->row = NULL; 1828 data->node = node; 1829 data->summaryview = summaryview; 1830 data->display_msg = display_msg; 1831 debug_print("postponing open of message till end of load\n"); 1832 g_timeout_add(100, summary_select_retry, data); 1833 return; 1834 } 1835 if (summary_is_locked(summaryview)) { 1836 return; 1837 } 1838 if (!summaryview->folder_item) 1839 return; 1840 if (node) { 1841 gtkut_ctree_expand_parent_all(ctree, node); 1842 1843 summary_lock(summaryview); 1844 GTK_EVENTS_FLUSH(); 1845 summary_unlock(summaryview); 1846 1847 gtkut_ctree_node_move_if_on_the_edge(ctree, node, -1); 1848 1849 if (display_msg && summaryview->displayed == node) 1850 summaryview->displayed = NULL; 1851 summaryview->display_msg = display_msg; 1852 gtk_sctree_select(GTK_SCTREE(ctree), node); 1853 if (summaryview->selected == NULL) 1854 summaryview->selected = node; 1855 } 1856 } 1857 1858 guint summary_get_msgnum(SummaryView *summaryview, GtkCMCTreeNode *node) 1859 { 1860 GtkCMCTree *ctree =NULL; 1861 MsgInfo *msginfo; 1862 1863 if (!summaryview) 1864 return 0; 1865 ctree = GTK_CMCTREE(summaryview->ctree); 1866 if (!node) 1867 return 0; 1868 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1869 if (msginfo) 1870 return msginfo->msgnum; 1871 else 1872 return -1; 1873 } 1874 1875 static GtkCMCTreeNode *summary_find_prev_msg(SummaryView *summaryview, 1876 GtkCMCTreeNode *current_node, 1877 gboolean start_from_prev) 1878 { 1879 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1880 GtkCMCTreeNode *node; 1881 MsgInfo *msginfo; 1882 1883 if (current_node) { 1884 if (start_from_prev) 1885 node = gtkut_ctree_node_prev(ctree, current_node); 1886 else 1887 node = current_node; 1888 } else 1889 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1); 1890 1891 for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) { 1892 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1893 if (msginfo && !MSG_IS_DELETED(msginfo->flags)) break; 1894 } 1895 1896 return node; 1897 } 1898 1899 static GtkCMCTreeNode *summary_find_next_msg(SummaryView *summaryview, 1900 GtkCMCTreeNode *current_node, 1901 gboolean start_from_next) 1902 { 1903 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1904 GtkCMCTreeNode *node; 1905 MsgInfo *msginfo; 1906 1907 if (current_node) { 1908 if (start_from_next) 1909 node = gtkut_ctree_node_next(ctree, current_node); 1910 else 1911 node = current_node; 1912 } else 1913 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 1914 1915 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 1916 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1917 if (msginfo && !MSG_IS_DELETED(msginfo->flags) 1918 && !MSG_IS_MOVE(msginfo->flags)) break; 1919 } 1920 1921 return node; 1922 } 1923 1924 static GtkCMCTreeNode *summary_find_prev_flagged_msg(SummaryView *summaryview, 1925 GtkCMCTreeNode *current_node, 1926 MsgPermFlags flags, 1927 gboolean start_from_prev) 1928 { 1929 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1930 GtkCMCTreeNode *node; 1931 MsgInfo *msginfo; 1932 1933 if (current_node) { 1934 if (start_from_prev) 1935 node = gtkut_ctree_node_prev(ctree, current_node); 1936 else 1937 node = current_node; 1938 } else 1939 node = gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->rows - 1); 1940 1941 for (; node != NULL; node = gtkut_ctree_node_prev(ctree, node)) { 1942 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1943 if (msginfo && (msginfo->flags.perm_flags & flags) != 0) break; 1944 } 1945 1946 return node; 1947 } 1948 1949 static GtkCMCTreeNode *summary_find_next_flagged_msg(SummaryView *summaryview, 1950 GtkCMCTreeNode *current_node, 1951 MsgPermFlags flags, 1952 gboolean start_from_next) 1953 { 1954 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1955 GtkCMCTreeNode *node; 1956 MsgInfo *msginfo; 1957 1958 if (current_node) { 1959 if (start_from_next) 1960 node = gtkut_ctree_node_next(ctree, current_node); 1961 else 1962 node = current_node; 1963 } else 1964 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 1965 1966 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 1967 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1968 /* Find msg with matching flags but ignore messages with 1969 ignore flags, if searching for new or unread messages */ 1970 if ((msginfo && (msginfo->flags.perm_flags & flags) != 0) && 1971 !(((flags & (MSG_NEW | MSG_UNREAD)) != 0) && MSG_IS_IGNORE_THREAD(msginfo->flags)) 1972 ) 1973 break; 1974 } 1975 1976 return node; 1977 } 1978 1979 static GtkCMCTreeNode *summary_find_msg_by_msgnum(SummaryView *summaryview, 1980 guint msgnum) 1981 { 1982 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 1983 GtkCMCTreeNode *node; 1984 MsgInfo *msginfo; 1985 1986 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 1987 1988 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 1989 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 1990 if (msginfo && msginfo->msgnum == msgnum) break; 1991 } 1992 1993 return node; 1994 } 1995 1996 static guint attract_hash_func(gconstpointer key) 1997 { 1998 gchar *str; 1999 gchar *p; 2000 guint h; 2001 2002 Xstrdup_a(str, (const gchar *)key, return 0); 2003 trim_subject(str); 2004 2005 p = str; 2006 h = *p; 2007 2008 if (h) { 2009 for (p += 1; *p != '\0'; p++) 2010 h = (h << 5) - h + *p; 2011 } 2012 2013 return h; 2014 } 2015 2016 static gint attract_compare_func(gconstpointer a, gconstpointer b) 2017 { 2018 return subject_compare((const gchar *)a, (const gchar *)b) == 0; 2019 } 2020 2021 void summary_attract_by_subject(SummaryView *summaryview) 2022 { 2023 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 2024 GtkCMCList *clist = GTK_CMCLIST(ctree); 2025 GtkCMCTreeNode *src_node; 2026 GtkCMCTreeNode *dst_node, *sibling; 2027 GtkCMCTreeNode *tmp; 2028 MsgInfo *src_msginfo, *dst_msginfo; 2029 GHashTable *subject_table; 2030 2031 debug_print("Attracting messages by subject...\n"); 2032 STATUSBAR_PUSH(summaryview->mainwin, 2033 _("Attracting messages by subject...")); 2034 2035 main_window_cursor_wait(summaryview->mainwin); 2036 summary_freeze(summaryview); 2037 2038 subject_table = g_hash_table_new(attract_hash_func, 2039 attract_compare_func); 2040 2041 for (src_node = GTK_CMCTREE_NODE(clist->row_list); 2042 src_node != NULL; 2043 src_node = tmp) { 2044 tmp = GTK_CMCTREE_ROW(src_node)->sibling; 2045 src_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(src_node); 2046 if (!src_msginfo) continue; 2047 if (!src_msginfo->subject) continue; 2048 2049 /* find attracting node */ 2050 dst_node = g_hash_table_lookup(subject_table, 2051 src_msginfo->subject); 2052 2053 if (dst_node) { 2054 dst_msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(dst_node); 2055 2056 /* if the time difference is more than 20 days, 2057 don't attract */ 2058 if (ABS(src_msginfo->date_t - dst_msginfo->date_t) 2059 > 60 * 60 * 24 * 20) 2060 continue; 2061 2062 sibling = GTK_CMCTREE_ROW(dst_node)->sibling; 2063 if (src_node != sibling) 2064 gtk_cmctree_move(ctree, src_node, NULL, sibling); 2065 } 2066 2067 g_hash_table_insert(subject_table, 2068 src_msginfo->subject, src_node); 2069 } 2070 2071 g_hash_table_destroy(subject_table); 2072 2073 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 2074 2075 summary_thaw(summaryview); 2076 2077 debug_print("Attracting messages by subject done.\n"); 2078 STATUSBAR_POP(summaryview->mainwin); 2079 2080 main_window_cursor_normal(summaryview->mainwin); 2081 } 2082 2083 static void summary_free_msginfo_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 2084 gpointer data) 2085 { 2086 MsgInfo *msginfo = gtk_cmctree_node_get_row_data(ctree, node); 2087 2088 if (msginfo) 2089 procmsg_msginfo_free(&msginfo); 2090 } 2091 2092 static void summary_set_marks_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 2093 gpointer data) 2094 { 2095 SummaryView *summaryview = data; 2096 MsgInfo *msginfo; 2097 2098 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 2099 2100 cm_return_if_fail(msginfo != NULL); 2101 2102 if (MSG_IS_DELETED(msginfo->flags)) 2103 summaryview->deleted++; 2104 2105 summaryview->total_size += msginfo->size; 2106 2107 summary_set_row_marks(summaryview, node); 2108 } 2109 2110 static void summary_update_status(SummaryView *summaryview) 2111 { 2112 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 2113 GtkCMCTreeNode *node; 2114 MsgInfo *msginfo; 2115 2116 summaryview->total_size = 2117 summaryview->deleted = summaryview->moved = summaryview->copied = 0; 2118 2119 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 2120 node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 2121 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 2122 2123 if (!msginfo) 2124 continue; 2125 2126 if (MSG_IS_DELETED(msginfo->flags)) 2127 summaryview->deleted++; 2128 if (MSG_IS_MOVE(msginfo->flags)) 2129 summaryview->moved++; 2130 if (MSG_IS_COPY(msginfo->flags)) 2131 summaryview->copied++; 2132 summaryview->total_size += msginfo->size; 2133 } 2134 } 2135 2136 static void summary_status_show(SummaryView *summaryview) 2137 { 2138 gchar *str; 2139 gchar *del, *mv, *cp; 2140 gchar *sel; 2141 gchar *spc; 2142 gchar *itstr; 2143 GList *rowlist, *cur; 2144 guint n_selected = 0, n_new = 0, n_unread = 0, n_total = 0; 2145 goffset sel_size = 0, n_size = 0; 2146 MsgInfo *msginfo; 2147 gchar *name; 2148 2149 if (!summaryview->folder_item) { 2150 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), ""); 2151 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), ""); 2152 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), ""); 2153 toolbar_main_set_sensitive(summaryview->mainwin); 2154 return; 2155 } 2156 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_folder), PANGO_ELLIPSIZE_END); 2157 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_select), PANGO_ELLIPSIZE_END); 2158 gtk_label_set_ellipsize(GTK_LABEL(summaryview->statlabel_msgs), PANGO_ELLIPSIZE_START); 2159 2160 rowlist = GTK_CMCLIST(summaryview->ctree)->selection; 2161 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) { 2162 msginfo = gtk_cmctree_node_get_row_data 2163 (GTK_CMCTREE(summaryview->ctree), 2164 GTK_CMCTREE_NODE(cur->data)); 2165 if (msginfo) { 2166 sel_size += msginfo->size; 2167 n_selected++; 2168 } 2169 } 2170 2171 if (summaryview->folder_item->hide_read_msgs 2172 || summaryview->folder_item->hide_del_msgs 2173 || summaryview->folder_item->hide_read_threads) { 2174 rowlist = GTK_CMCLIST(summaryview->ctree)->row_list; 2175 for (cur = rowlist; cur != NULL && cur->data != NULL; cur = cur->next) { 2176 msginfo = gtk_cmctree_node_get_row_data 2177 (GTK_CMCTREE(summaryview->ctree), 2178 GTK_CMCTREE_NODE(cur)); 2179 if (msginfo) { 2180 n_size += msginfo->size; 2181 n_total++; 2182 if (MSG_IS_NEW(msginfo->flags)) 2183 n_new++; 2184 if (MSG_IS_UNREAD(msginfo->flags)) 2185 n_unread++; 2186 } 2187 } 2188 } else { 2189 n_new = summaryview->folder_item->new_msgs; 2190 n_unread = summaryview->folder_item->unread_msgs; 2191 } 2192 2193 name = folder_item_get_name(summaryview->folder_item); 2194 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name); 2195 g_free(name); 2196 2197 if (summaryview->deleted) 2198 del = g_strdup_printf(_("%d deleted"), summaryview->deleted); 2199 else 2200 del = g_strdup(""); 2201 if (summaryview->moved) 2202 mv = g_strdup_printf(_("%s%d moved"), 2203 summaryview->deleted ? ", " : "", 2204 summaryview->moved); 2205 else 2206 mv = g_strdup(""); 2207 if (summaryview->copied) 2208 cp = g_strdup_printf(_("%s%d copied"), 2209 summaryview->deleted || 2210 summaryview->moved ? ", " : "", 2211 summaryview->copied); 2212 else 2213 cp = g_strdup(""); 2214 2215 if (summaryview->deleted || summaryview->moved || summaryview->copied) 2216 spc = " "; 2217 else 2218 spc = ""; 2219 2220 if (n_selected) { 2221 sel = g_strdup_printf(" (%s)", to_human_readable((goffset)sel_size)); 2222 itstr = strdup(" items selected"); 2223 } else { 2224 sel = g_strdup(""); 2225 itstr = g_strdup(""); 2226 } 2227 2228 str = g_strconcat(n_selected ? itos(n_selected) : "", 2229 itstr, sel, spc, del, mv, cp, NULL); 2230 g_free(sel); 2231 g_free(del); 2232 g_free(mv); 2233 g_free(cp); 2234 g_free(itstr); 2235 2236 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_select), str); 2237 g_free(str); 2238 2239 str = g_strdup_printf(_("%d new, %d unread, %d total (%s)"), 2240 n_new, n_unread, n_total, 2241 to_human_readable((goffset)n_size)); 2242 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_msgs), str); 2243 g_free(str); 2244 2245 summary_set_menu_sensitive(summaryview); 2246 toolbar_main_set_sensitive(summaryview->mainwin); 2247 } 2248 2249 static void summary_set_column_titles(SummaryView *summaryview) 2250 { 2251 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 2252 GtkWidget *hbox; 2253 GtkWidget *label; 2254 GtkWidget *arrow; 2255 gint pos; 2256 const gchar *title; 2257 SummaryColumnType type; 2258 GtkJustification justify; 2259 2260 static FolderSortKey sort_by[N_SUMMARY_COLS] = { 2261 SORT_BY_MARK, 2262 SORT_BY_STATUS, 2263 SORT_BY_MIME, 2264 SORT_BY_SUBJECT, 2265 SORT_BY_FROM, 2266 SORT_BY_TO, 2267 SORT_BY_DATE, 2268 SORT_BY_SIZE, 2269 SORT_BY_NUMBER, 2270 SORT_BY_SCORE, 2271 SORT_BY_LOCKED, 2272 SORT_BY_TAGS 2273 }; 2274 2275 for (pos = 0; pos < N_SUMMARY_COLS; pos++) { 2276 type = summaryview->col_state[pos].type; 2277 title = col_label[type]; 2278 2279 /* CLAWS: mime and unread are single char headers */ 2280 justify = (type == S_COL_NUMBER || type == S_COL_SIZE) 2281 ? GTK_JUSTIFY_RIGHT : GTK_JUSTIFY_LEFT; 2282 2283 if (type == S_COL_MIME) { 2284 label = gtk_image_new_from_pixbuf(clipxpm); 2285 gtk_widget_show(label); 2286 gtk_cmclist_set_column_widget(clist, pos, label); 2287 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Attachment")); 2288 continue; 2289 } else if (type == S_COL_MARK) { 2290 label = gtk_image_new_from_pixbuf(markxpm); 2291 gtk_widget_show(label); 2292 gtk_cmclist_set_column_widget(clist, pos, label); 2293 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, C_("Column Header", "_Mark")); 2294 continue; 2295 } else if (type == S_COL_LOCKED) { 2296 label = gtk_image_new_from_pixbuf(lockedxpm); 2297 gtk_widget_show(label); 2298 gtk_cmclist_set_column_widget(clist, pos, label); 2299 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Locked")); 2300 continue; 2301 } else if (type == S_COL_STATUS) { 2302 gtk_cmclist_set_column_title(clist, pos, title); 2303 gtk_sctree_set_column_tooltip(GTK_SCTREE(clist), pos, _("Status")); 2304 continue; 2305 } 2306 2307 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); 2308 label = gtk_label_new(title); 2309 2310 if (justify == GTK_JUSTIFY_RIGHT) 2311 gtk_box_pack_end(GTK_BOX(hbox), label, 2312 FALSE, FALSE, 0); 2313 else 2314 gtk_box_pack_start(GTK_BOX(hbox), label, 2315 FALSE, FALSE, 0); 2316 2317 if (summaryview->sort_key == sort_by[type] || 2318 (summaryview->sort_key == SORT_BY_THREAD_DATE && 2319 sort_by[SORT_BY_DATE] && type == S_COL_DATE)) { 2320 arrow = gtk_image_new_from_icon_name 2321 (summaryview->sort_type == SORT_ASCENDING 2322 ? "pan-down-symbolic" : "pan-up-symbolic", 2323 GTK_ICON_SIZE_MENU); 2324 gtk_widget_set_size_request(GTK_WIDGET(arrow), 10, 10); 2325 if (justify == GTK_JUSTIFY_RIGHT) 2326 gtk_box_pack_start(GTK_BOX(hbox), arrow, 2327 FALSE, FALSE, 0); 2328 else 2329 gtk_box_pack_end(GTK_BOX(hbox), arrow, 2330 FALSE, FALSE, 0); 2331 } 2332 2333 gtk_widget_show_all(hbox); 2334 gtk_cmclist_set_column_widget(clist, pos, hbox); 2335 } 2336 } 2337 2338 void summary_sort(SummaryView *summaryview, 2339 FolderSortKey sort_key, FolderSortType sort_type) 2340 { 2341 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 2342 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 2343 GtkCMCListCompareFunc cmp_func = NULL; 2344 g_signal_handlers_block_by_func(G_OBJECT(summaryview->ctree), 2345 G_CALLBACK(summary_tree_expanded), summaryview); 2346 summary_freeze(summaryview); 2347 2348 switch (sort_key) { 2349 case SORT_BY_MARK: 2350 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mark; 2351 break; 2352 case SORT_BY_STATUS: 2353 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_status; 2354 break; 2355 case SORT_BY_MIME: 2356 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_mime; 2357 break; 2358 case SORT_BY_NUMBER: 2359 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_num; 2360 break; 2361 case SORT_BY_SIZE: 2362 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_size; 2363 break; 2364 case SORT_BY_DATE: 2365 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_date; 2366 break; 2367 case SORT_BY_THREAD_DATE: 2368 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_thread_date; 2369 break; 2370 case SORT_BY_FROM: 2371 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_from; 2372 break; 2373 case SORT_BY_SUBJECT: 2374 if (summaryview->simplify_subject_preg) 2375 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_simplified_subject; 2376 else 2377 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_subject; 2378 break; 2379 case SORT_BY_SCORE: 2380 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_score; 2381 break; 2382 case SORT_BY_LABEL: 2383 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_label; 2384 break; 2385 case SORT_BY_TO: 2386 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_to; 2387 break; 2388 case SORT_BY_LOCKED: 2389 cmp_func = (GtkCMCListCompareFunc)summary_cmp_by_locked; 2390 break; 2391 case SORT_BY_NONE: 2392 break; 2393 default: 2394 goto unlock; 2395 } 2396 2397 summaryview->sort_key = sort_key; 2398 summaryview->sort_type = sort_type; 2399 2400 summary_set_column_titles(summaryview); 2401 summary_set_menu_sensitive(summaryview); 2402 2403 /* allow fallback to don't sort */ 2404 if (summaryview->sort_key == SORT_BY_NONE) 2405 goto unlock; 2406 2407 if (cmp_func != NULL) { 2408 debug_print("Sorting summary...\n"); 2409 STATUSBAR_PUSH(summaryview->mainwin, _("Sorting summary...")); 2410 2411 main_window_cursor_wait(summaryview->mainwin); 2412 2413 gtk_cmclist_set_compare_func(clist, cmp_func); 2414 2415 gtk_cmclist_set_sort_type(clist, (GtkSortType)sort_type); 2416 gtk_sctree_sort_recursive(ctree, NULL); 2417 2418 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 2419 2420 main_window_cursor_normal(summaryview->mainwin); 2421 2422 debug_print("Sorting summary done.\n"); 2423 STATUSBAR_POP(summaryview->mainwin); 2424 } 2425 unlock: 2426 summary_thaw(summaryview); 2427 g_signal_handlers_unblock_by_func(G_OBJECT(summaryview->ctree), 2428 G_CALLBACK(summary_tree_expanded), summaryview); 2429 } 2430 2431 static gboolean summary_update_thread_age(GNode *node, gpointer data) 2432 { 2433 MsgInfo *msginfo = node->data; 2434 time_t *most_recent = (time_t *)data; 2435 2436 if (msginfo->date_t > *most_recent) { 2437 *most_recent = msginfo->date_t; 2438 } 2439 return FALSE; 2440 } 2441 2442 static void summary_find_thread_age(GNode *gnode) 2443 { 2444 MsgInfo *msginfo = (MsgInfo *)gnode->data; 2445 time_t most_recent; 2446 2447 if (!msginfo) 2448 return; 2449 most_recent = msginfo->thread_date = msginfo->date_t; 2450 2451 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_thread_age, &most_recent); 2452 2453 msginfo->thread_date = most_recent; 2454 } 2455 2456 static gboolean summary_update_is_read(GNode *node, gpointer data) 2457 { 2458 MsgInfo *msginfo = node->data; 2459 gboolean *all_read = (gboolean *)data; 2460 2461 if (MSG_IS_UNREAD(msginfo->flags)) { 2462 *all_read = FALSE; 2463 return TRUE; 2464 } 2465 return FALSE; 2466 } 2467 2468 static gboolean summary_thread_is_read(GNode *gnode) 2469 { 2470 MsgInfo *msginfo = (MsgInfo *)gnode->data; 2471 gboolean all_read = TRUE; 2472 2473 if (!msginfo) 2474 return all_read; 2475 2476 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, summary_update_is_read, &all_read); 2477 return all_read; 2478 } 2479 2480 typedef struct _ThreadSelectedData { 2481 guint msgnum; 2482 gboolean is_selected; 2483 } ThreadSelectedData; 2484 2485 static gboolean summary_update_is_selected(GNode *gnode, gpointer data) 2486 { 2487 ThreadSelectedData *selected = (ThreadSelectedData *)data; 2488 MsgInfo *msginfo = (MsgInfo *)gnode->data; 2489 2490 if (msginfo->msgnum == selected->msgnum) { 2491 selected->is_selected = TRUE; 2492 return TRUE; 2493 } 2494 2495 return FALSE; 2496 } 2497 2498 static gboolean summary_thread_is_selected(GNode *gnode, guint selected_msgnum) 2499 { 2500 ThreadSelectedData selected; 2501 2502 selected.msgnum = selected_msgnum; 2503 selected.is_selected = FALSE; 2504 g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, 2505 summary_update_is_selected, &selected); 2506 return selected.is_selected; 2507 } 2508 2509 static gboolean summary_insert_gnode_func(GtkCMCTree *ctree, guint depth, GNode *gnode, 2510 GtkCMCTreeNode *cnode, gpointer data) 2511 { 2512 SummaryView *summaryview = (SummaryView *)data; 2513 MsgInfo *msginfo = (MsgInfo *)gnode->data; 2514 gchar *text[N_SUMMARY_COLS]; 2515 gint *col_pos = summaryview->col_pos; 2516 const gchar *msgid = msginfo->msgid; 2517 GHashTable *msgid_table = summaryview->msgid_table; 2518 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT); 2519 2520 summary_set_header(summaryview, text, msginfo); 2521 2522 gtk_cmctree_set_node_info(ctree, cnode, text[col_pos[S_COL_SUBJECT]], 2, 2523 NULL, NULL, FALSE, summaryview->threaded && !summaryview->thread_collapsed); 2524 #define SET_TEXT(col) { \ 2525 gtk_cmctree_node_set_text(ctree, cnode, col_pos[col], \ 2526 text[col_pos[col]]); \ 2527 } 2528 2529 if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible) 2530 SET_TEXT(S_COL_NUMBER); 2531 if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible) 2532 SET_TEXT(S_COL_SCORE); 2533 if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible) 2534 SET_TEXT(S_COL_SIZE); 2535 if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible) 2536 SET_TEXT(S_COL_DATE); 2537 if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible) 2538 SET_TEXT(S_COL_FROM); 2539 if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) 2540 SET_TEXT(S_COL_TO); 2541 if (summaryview->col_state[summaryview->col_pos[S_COL_TAGS]].visible) 2542 SET_TEXT(S_COL_TAGS); 2543 2544 if (vert_layout && prefs_common.two_line_vert) 2545 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]); 2546 2547 #undef SET_TEXT 2548 2549 GTKUT_CTREE_NODE_SET_ROW_DATA(cnode, msginfo); 2550 summary_set_marks_func(ctree, cnode, summaryview); 2551 2552 if (msgid && msgid[0] != '\0') 2553 g_hash_table_insert(msgid_table, (gchar *)msgid, cnode); 2554 2555 return TRUE; 2556 } 2557 2558 static void summary_set_ctree_from_list(SummaryView *summaryview, 2559 GSList *mlist, guint selected_msgnum) 2560 { 2561 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 2562 MsgInfo *msginfo; 2563 GtkCMCTreeNode *node = NULL; 2564 GHashTable *msgid_table; 2565 GHashTable *subject_table = NULL; 2566 GSList * cur; 2567 GdkDisplay *display; 2568 2569 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT); 2570 2571 if (!mlist) return; 2572 2573 display = gdk_display_get_default(); 2574 2575 debug_print("Setting summary from message data...\n"); 2576 STATUSBAR_PUSH(summaryview->mainwin, 2577 _("Setting summary from message data...")); 2578 gdk_display_flush(display); 2579 2580 g_signal_handlers_block_by_func(G_OBJECT(ctree), 2581 G_CALLBACK(summary_tree_expanded), summaryview); 2582 2583 msgid_table = g_hash_table_new(g_str_hash, g_str_equal); 2584 summaryview->msgid_table = msgid_table; 2585 2586 if (prefs_common.thread_by_subject) { 2587 subject_table = g_hash_table_new(g_str_hash, g_str_equal); 2588 summaryview->subject_table = subject_table; 2589 } else { 2590 summaryview->subject_table = NULL; 2591 } 2592 2593 if (prefs_common.use_addr_book) 2594 start_address_completion(NULL); 2595 2596 if (summaryview->threaded) { 2597 GNode *root, *gnode; 2598 root = procmsg_get_thread_tree(mlist); 2599 2600 2601 for (gnode = root->children; gnode != NULL; 2602 gnode = gnode->next) { 2603 if (!summaryview->folder_item->hide_read_threads || 2604 !summary_thread_is_read(gnode) || 2605 summary_thread_is_selected(gnode, selected_msgnum)) { 2606 summary_find_thread_age(gnode); 2607 node = gtk_sctree_insert_gnode 2608 (ctree, NULL, node, gnode, 2609 summary_insert_gnode_func, summaryview); 2610 } 2611 } 2612 2613 g_node_destroy(root); 2614 2615 } else { 2616 gchar *text[N_SUMMARY_COLS]; 2617 cur = mlist; 2618 for (; mlist != NULL; mlist = mlist->next) { 2619 msginfo = (MsgInfo *)mlist->data; 2620 2621 summary_set_header(summaryview, text, msginfo); 2622 2623 node = gtk_sctree_insert_node 2624 (ctree, NULL, node, text, 2, 2625 NULL, NULL, 2626 FALSE, FALSE); 2627 if (vert_layout && prefs_common.two_line_vert) 2628 g_free(text[summaryview->col_pos[S_COL_SUBJECT]]); 2629 2630 GTKUT_CTREE_NODE_SET_ROW_DATA(node, msginfo); 2631 summary_set_marks_func(ctree, node, summaryview); 2632 2633 if (msginfo->msgid && msginfo->msgid[0] != '\0') 2634 g_hash_table_insert(msgid_table, 2635 msginfo->msgid, node); 2636 2637 if (prefs_common.thread_by_subject) 2638 subject_table_insert(subject_table, 2639 msginfo->subject, 2640 node); 2641 } 2642 mlist = cur; 2643 } 2644 2645 if (prefs_common.enable_hscrollbar && 2646 summaryview->col_pos[S_COL_SUBJECT] == N_SUMMARY_COLS - 1) { 2647 gint optimal_width; 2648 2649 optimal_width = gtk_cmclist_optimal_column_width 2650 (GTK_CMCLIST(ctree), summaryview->col_pos[S_COL_SUBJECT]); 2651 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), 2652 summaryview->col_pos[S_COL_SUBJECT], 2653 optimal_width); 2654 } 2655 2656 if (prefs_common.use_addr_book) 2657 end_address_completion(); 2658 2659 debug_print("Setting summary from message data done.\n"); 2660 STATUSBAR_POP(summaryview->mainwin); 2661 if (debug_get_mode()) { 2662 debug_print("\tmsgid hash table size = %d\n", 2663 g_hash_table_size(msgid_table)); 2664 if (prefs_common.thread_by_subject) 2665 debug_print("\tsubject hash table size = %d\n", 2666 g_hash_table_size(subject_table)); 2667 } 2668 2669 summary_sort(summaryview, summaryview->sort_key, summaryview->sort_type); 2670 2671 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 2672 2673 if (prefs_common.bold_unread || prefs_common.bold_marked) { 2674 while (node) { 2675 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node); 2676 if (GTK_CMCTREE_ROW(node)->children) 2677 summary_set_row_marks(summaryview, node); 2678 node = next; 2679 } 2680 } 2681 2682 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), 2683 G_CALLBACK(summary_tree_expanded), summaryview); 2684 } 2685 2686 static gchar *summary_complete_address(const gchar *addr) 2687 { 2688 gint count; 2689 gchar *res, *tmp, *email_addr; 2690 2691 if (addr == NULL || !strchr(addr, '@')) 2692 return NULL; 2693 2694 Xstrdup_a(email_addr, addr, return NULL); 2695 extract_address(email_addr); 2696 if (!*email_addr) 2697 return NULL; 2698 2699 /* 2700 * completion stuff must be already initialized 2701 */ 2702 res = NULL; 2703 if (1 < (count = complete_address(email_addr))) { 2704 tmp = get_complete_address(1); 2705 res = procheader_get_fromname(tmp); 2706 g_free(tmp); 2707 } 2708 2709 return res; 2710 } 2711 2712 static inline void summary_set_header(SummaryView *summaryview, gchar *text[], 2713 MsgInfo *msginfo) 2714 { 2715 static gchar date_modified[80]; 2716 static gchar col_score[11]; 2717 static gchar from_buf[BUFFSIZE], to_buf[BUFFSIZE]; 2718 static gchar tmp2[BUFFSIZE+4], tmp3[BUFFSIZE]; 2719 gint *col_pos = summaryview->col_pos; 2720 gchar *from_text = NULL, *to_text = NULL; 2721 gboolean should_swap = FALSE; 2722 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT); 2723 static const gchar *color_dim_rgb = NULL; 2724 if (!color_dim_rgb) 2725 color_dim_rgb = gtkut_gdk_rgba_to_string(&summaryview->color_dim); 2726 text[col_pos[S_COL_FROM]] = ""; 2727 text[col_pos[S_COL_TO]] = ""; 2728 text[col_pos[S_COL_SUBJECT]]= ""; 2729 text[col_pos[S_COL_MARK]] = ""; 2730 text[col_pos[S_COL_STATUS]] = ""; 2731 text[col_pos[S_COL_MIME]] = ""; 2732 text[col_pos[S_COL_LOCKED]] = ""; 2733 text[col_pos[S_COL_DATE]] = ""; 2734 text[col_pos[S_COL_TAGS]] = ""; 2735 if (summaryview->col_state[summaryview->col_pos[S_COL_NUMBER]].visible) 2736 text[col_pos[S_COL_NUMBER]] = itos(msginfo->msgnum); 2737 else 2738 text[col_pos[S_COL_NUMBER]] = ""; 2739 2740 /* slow! */ 2741 if (summaryview->col_state[summaryview->col_pos[S_COL_SIZE]].visible) 2742 text[col_pos[S_COL_SIZE]] = to_human_readable(msginfo->size); 2743 else 2744 text[col_pos[S_COL_SIZE]] = ""; 2745 2746 if (summaryview->col_state[summaryview->col_pos[S_COL_SCORE]].visible) 2747 text[col_pos[S_COL_SCORE]] = itos_buf(col_score, msginfo->score); 2748 else 2749 text[col_pos[S_COL_SCORE]] = ""; 2750 2751 /* slow! */ 2752 if (summaryview->col_state[summaryview->col_pos[S_COL_DATE]].visible || 2753 (vert_layout && prefs_common.two_line_vert)) { 2754 if (msginfo->date_t && msginfo->date_t > 0) { 2755 procheader_date_get_localtime(date_modified, 2756 sizeof(date_modified), 2757 msginfo->date_t); 2758 text[col_pos[S_COL_DATE]] = date_modified; 2759 } else if (msginfo->date) 2760 text[col_pos[S_COL_DATE]] = msginfo->date; 2761 else 2762 text[col_pos[S_COL_DATE]] = _("(No Date)"); 2763 } 2764 2765 if (prefs_common.swap_from && msginfo->from && msginfo->to 2766 && !summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) { 2767 gchar *addr = NULL; 2768 2769 addr = g_strdup(msginfo->from); 2770 2771 if (addr) { 2772 extract_address(addr); 2773 if (account_find_from_address(addr)) { 2774 should_swap = TRUE; 2775 } 2776 g_free(addr); 2777 } 2778 } 2779 2780 if (!prefs_common.use_addr_book) { 2781 if (prefs_common.summary_from_show == SHOW_NAME) 2782 from_text = msginfo->fromname; 2783 else if (prefs_common.summary_from_show == SHOW_BOTH) 2784 from_text = msginfo->from; 2785 else { 2786 from_text = msginfo->from; 2787 extract_address(from_text); 2788 } 2789 if (!from_text) 2790 from_text = _("(No From)"); 2791 } else { 2792 gchar *tmp = summary_complete_address(msginfo->from); 2793 if (tmp) { 2794 strncpy2(from_buf, tmp, sizeof(from_buf)); 2795 g_free(tmp); 2796 from_text = from_buf; 2797 } else { 2798 if (prefs_common.summary_from_show == SHOW_NAME) 2799 from_text = msginfo->fromname; 2800 else if (prefs_common.summary_from_show == SHOW_BOTH) 2801 from_text = msginfo->from; 2802 else { 2803 from_text = msginfo->from; 2804 if (from_text) 2805 extract_address(from_text); 2806 } 2807 if (!from_text) 2808 from_text = _("(No From)"); 2809 } 2810 } 2811 2812 to_text = msginfo->to ? msginfo->to : 2813 (msginfo->cc ? msginfo->cc : NULL); 2814 2815 if (!to_text) 2816 to_text = _("(No Recipient)"); 2817 else { 2818 if (prefs_common.summary_from_show == SHOW_NAME) { 2819 gchar *tmp = procheader_get_fromname(to_text); 2820 /* need to keep to_text pointing to stack, so heap-allocated 2821 * string from procheader_get_fromname() will be copied to to_buf */ 2822 if (tmp != NULL) { 2823 strncpy2(to_buf, tmp, sizeof(to_buf)); 2824 g_free(tmp); 2825 to_text = to_buf; 2826 } 2827 } else if (prefs_common.summary_from_show == SHOW_ADDR) 2828 extract_address(to_text); 2829 } 2830 2831 text[col_pos[S_COL_TO]] = to_text; 2832 if (!should_swap) { 2833 text[col_pos[S_COL_FROM]] = from_text; 2834 } else { 2835 if (prefs_common.use_addr_book) { 2836 gchar *tmp = summary_complete_address(to_text); 2837 /* need to keep to_text pointing to stack, so heap-allocated 2838 * string from summary_complete_address() will be copied to to_buf */ 2839 if (tmp) { 2840 strncpy2(to_buf, tmp, sizeof(to_buf)); 2841 g_free(tmp); 2842 to_text = to_buf; 2843 } else { 2844 to_text = to_text ? to_text : _("(No From)"); 2845 } 2846 } 2847 snprintf(tmp2, BUFFSIZE+4, "➜ %s", to_text); 2848 tmp2[BUFFSIZE-1]='\0'; 2849 text[col_pos[S_COL_FROM]] = tmp2; 2850 } 2851 2852 if (summaryview->simplify_subject_preg != NULL) 2853 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? 2854 string_remove_match(tmp3, BUFFSIZE, msginfo->subject, 2855 summaryview->simplify_subject_preg) : 2856 _("(No Subject)"); 2857 else 2858 text[col_pos[S_COL_SUBJECT]] = msginfo->subject ? msginfo->subject : 2859 _("(No Subject)"); 2860 if (vert_layout && prefs_common.two_line_vert) { 2861 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) { 2862 gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n", 2863 "<span color='%s' style='italic'>", 2864 _("From: %s, on %s"), "</span>", NULL), 2865 text[col_pos[S_COL_SUBJECT]], 2866 color_dim_rgb, 2867 text[col_pos[S_COL_FROM]], 2868 text[col_pos[S_COL_DATE]]); 2869 text[col_pos[S_COL_SUBJECT]] = tmp; 2870 } else { 2871 gchar *tmp = g_markup_printf_escaped(g_strconcat("%s\n", 2872 "<span color='%s' style='italic'>", 2873 _("To: %s, on %s"), "</span>", NULL), 2874 text[col_pos[S_COL_SUBJECT]], 2875 color_dim_rgb, 2876 text[col_pos[S_COL_TO]], 2877 text[col_pos[S_COL_DATE]]); 2878 text[col_pos[S_COL_SUBJECT]] = tmp; 2879 } 2880 } 2881 } 2882 2883 static void summary_display_msg(SummaryView *summaryview, GtkCMCTreeNode *row) 2884 { 2885 summary_display_msg_full(summaryview, row, FALSE, FALSE, FALSE); 2886 } 2887 2888 static gboolean defer_change(gpointer data); 2889 typedef enum { 2890 FLAGS_UNSET, 2891 FLAGS_SET, 2892 FLAGS_CHANGE 2893 } ChangeType; 2894 typedef struct _ChangeData { 2895 MsgInfo *info; 2896 ChangeType op; 2897 MsgPermFlags set_flags; 2898 MsgTmpFlags set_tmp_flags; 2899 MsgPermFlags unset_flags; 2900 MsgTmpFlags unset_tmp_flags; 2901 } ChangeData; 2902 2903 static void summary_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags) 2904 { 2905 if (!msginfo->folder || !msginfo->folder->processing_pending) { 2906 debug_print("flags: doing unset now\n"); 2907 procmsg_msginfo_unset_flags(msginfo, flags, tmp_flags); 2908 } else { 2909 ChangeData *unset_data = g_new0(ChangeData, 1); 2910 unset_data->info = msginfo; 2911 unset_data->op = FLAGS_UNSET; 2912 unset_data->unset_flags = flags; 2913 unset_data->unset_tmp_flags = tmp_flags; 2914 debug_print("flags: deferring unset\n"); 2915 g_timeout_add(100, defer_change, unset_data); 2916 } 2917 } 2918 2919 static void summary_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags flags, MsgTmpFlags tmp_flags) 2920 { 2921 if (!msginfo->folder || !msginfo->folder->processing_pending) { 2922 debug_print("flags: doing set now\n"); 2923 procmsg_msginfo_set_flags(msginfo, flags, tmp_flags); 2924 } else { 2925 ChangeData *set_data = g_new0(ChangeData, 1); 2926 set_data->info = msginfo; 2927 set_data->op = FLAGS_SET; 2928 set_data->set_flags = flags; 2929 set_data->set_tmp_flags = tmp_flags; 2930 debug_print("flags: deferring set\n"); 2931 g_timeout_add(100, defer_change, set_data); 2932 } 2933 } 2934 2935 static void summary_msginfo_change_flags(MsgInfo *msginfo, 2936 MsgPermFlags add_flags, MsgTmpFlags add_tmp_flags, 2937 MsgPermFlags rem_flags, MsgTmpFlags rem_tmp_flags) 2938 { 2939 if (!msginfo->folder || !msginfo->folder->processing_pending) { 2940 debug_print("flags: doing change now\n"); 2941 procmsg_msginfo_change_flags(msginfo, add_flags, add_tmp_flags, 2942 rem_flags, rem_tmp_flags); 2943 } else { 2944 ChangeData *change_data = g_new0(ChangeData, 1); 2945 change_data->info = msginfo; 2946 change_data->op = FLAGS_CHANGE; 2947 change_data->set_flags = add_flags; 2948 change_data->set_tmp_flags = add_tmp_flags; 2949 change_data->unset_flags = rem_flags; 2950 change_data->unset_tmp_flags = rem_tmp_flags; 2951 debug_print("flags: deferring change\n"); 2952 g_timeout_add(100, defer_change, change_data); 2953 } 2954 } 2955 2956 gboolean defer_change(gpointer data) 2957 { 2958 ChangeData *chg = (ChangeData *)data; 2959 if (chg->info->folder && chg->info->folder->processing_pending) { 2960 debug_print("flags: trying later\n"); 2961 return TRUE; /* try again */ 2962 } else { 2963 debug_print("flags: finally doing it\n"); 2964 switch(chg->op) { 2965 case FLAGS_UNSET: 2966 procmsg_msginfo_unset_flags(chg->info, chg->unset_flags, chg->unset_tmp_flags); 2967 break; 2968 case FLAGS_SET: 2969 procmsg_msginfo_set_flags(chg->info, chg->set_flags, chg->set_tmp_flags); 2970 break; 2971 case FLAGS_CHANGE: 2972 procmsg_msginfo_change_flags(chg->info, chg->set_flags, chg->set_tmp_flags, 2973 chg->unset_flags, chg->unset_tmp_flags); 2974 break; 2975 default: 2976 g_warning("unknown change op"); 2977 } 2978 g_free(chg); 2979 } 2980 return FALSE; 2981 } 2982 2983 static void msginfo_mark_as_read (SummaryView *summaryview, MsgInfo *msginfo, 2984 GtkCMCTreeNode *row) 2985 { 2986 cm_return_if_fail(summaryview != NULL); 2987 cm_return_if_fail(msginfo != NULL); 2988 cm_return_if_fail(row != NULL); 2989 2990 if (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) { 2991 summary_msginfo_unset_flags 2992 (msginfo, MSG_NEW | MSG_UNREAD, 0); 2993 summary_set_row_marks(summaryview, row); 2994 summary_status_show(summaryview); 2995 } 2996 } 2997 2998 typedef struct { 2999 MsgInfo *msginfo; 3000 SummaryView *summaryview; 3001 } MarkAsReadData; 3002 3003 static int msginfo_mark_as_read_timeout(void *data) 3004 { 3005 MarkAsReadData *mdata = (MarkAsReadData *)data; 3006 if (!mdata) 3007 return FALSE; 3008 3009 summary_lock(mdata->summaryview); 3010 if (mdata->msginfo == summary_get_selected_msg(mdata->summaryview)) 3011 msginfo_mark_as_read(mdata->summaryview, mdata->msginfo, 3012 mdata->summaryview->selected); 3013 procmsg_msginfo_free(&(mdata->msginfo)); 3014 3015 mdata->summaryview->mark_as_read_timeout_tag = 0; 3016 summary_unlock(mdata->summaryview); 3017 3018 g_free(mdata); 3019 return FALSE; 3020 } 3021 3022 static void summary_display_msg_full(SummaryView *summaryview, 3023 GtkCMCTreeNode *row, 3024 gboolean new_window, gboolean all_headers, 3025 gboolean from_sep_msgview) 3026 { 3027 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3028 MsgInfo *msginfo; 3029 gint val; 3030 if (!new_window) { 3031 if (summaryview->displayed == row && 3032 messageview_is_visible(summaryview->messageview)) 3033 return; 3034 else if (summaryview->messageview) 3035 summaryview->messageview->filtered = FALSE; 3036 } 3037 cm_return_if_fail(row != NULL); 3038 3039 if (summary_is_locked(summaryview)) return; 3040 summary_lock(summaryview); 3041 3042 STATUSBAR_POP(summaryview->mainwin); 3043 GTK_EVENTS_FLUSH(); 3044 3045 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3046 3047 if (!msginfo) { 3048 debug_print("NULL msginfo\n"); 3049 summary_unlock(summaryview); 3050 return; 3051 } 3052 3053 if (new_window) { 3054 MessageView *msgview = messageview_create_with_new_window(summaryview->mainwin); 3055 val = messageview_show(msgview, msginfo, all_headers); 3056 } else { 3057 MessageView *msgview = summaryview->messageview; 3058 summaryview->displayed = row; 3059 if (!messageview_is_visible(msgview) && 3060 gtk_window_is_active(GTK_WINDOW(summaryview->mainwin->window))) { 3061 main_window_toggle_message_view(summaryview->mainwin); 3062 GTK_EVENTS_FLUSH(); 3063 } 3064 val = messageview_show(msgview, msginfo, all_headers); 3065 if (mimeview_tree_is_empty(msgview->mimeview)) 3066 gtk_widget_grab_focus(summaryview->ctree); 3067 gtkut_ctree_node_move_if_on_the_edge(ctree, row, 3068 GTK_CMCLIST(summaryview->ctree)->focus_row); 3069 } 3070 3071 if (val == 0 && MSG_IS_UNREAD(msginfo->flags)) { 3072 if (!prefs_common.mark_as_read_on_new_window && 3073 prefs_common.mark_as_read_delay) { 3074 MarkAsReadData *data = g_new0(MarkAsReadData, 1); 3075 data->summaryview = summaryview; 3076 data->msginfo = procmsg_msginfo_new_ref(msginfo); 3077 if (summaryview->mark_as_read_timeout_tag != 0) 3078 g_source_remove(summaryview->mark_as_read_timeout_tag); 3079 summaryview->mark_as_read_timeout_tag = 3080 g_timeout_add_seconds(prefs_common.mark_as_read_delay, 3081 msginfo_mark_as_read_timeout, data); 3082 } else if (new_window || !prefs_common.mark_as_read_on_new_window) { 3083 msginfo_mark_as_read(summaryview, msginfo, row); 3084 } else if (from_sep_msgview && 3085 prefs_common.mark_as_read_on_new_window) { 3086 msginfo_mark_as_read(summaryview, msginfo, row); 3087 } 3088 } 3089 3090 summary_set_menu_sensitive(summaryview); 3091 toolbar_main_set_sensitive(summaryview->mainwin); 3092 messageview_set_menu_sensitive(summaryview->messageview); 3093 3094 summary_unlock(summaryview); 3095 } 3096 3097 void summary_display_msg_selected(SummaryView *summaryview, 3098 gboolean all_headers) 3099 { 3100 if (summary_is_locked(summaryview)) return; 3101 summaryview->displayed = NULL; 3102 summary_display_msg_full(summaryview, summaryview->selected, FALSE, 3103 all_headers, FALSE); 3104 } 3105 3106 void summary_redisplay_msg(SummaryView *summaryview) 3107 { 3108 GtkCMCTreeNode *node; 3109 3110 if (summaryview->displayed) { 3111 node = summaryview->displayed; 3112 summaryview->displayed = NULL; 3113 summary_display_msg(summaryview, node); 3114 } 3115 } 3116 3117 void summary_open_msg(SummaryView *summaryview, gboolean new_window, 3118 gboolean from_sep_msgview) 3119 { 3120 if (!summaryview->selected) return; 3121 3122 /* CLAWS: if separate message view, don't open a new window 3123 * but rather use the current separated message view */ 3124 summary_display_msg_full(summaryview, summaryview->selected, 3125 new_window, FALSE, from_sep_msgview); 3126 } 3127 3128 void summary_view_source(SummaryView * summaryview) 3129 { 3130 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3131 MsgInfo *msginfo; 3132 SourceWindow *srcwin; 3133 3134 if (!summaryview->selected) return; 3135 3136 srcwin = source_window_create(); 3137 msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected); 3138 source_window_show(srcwin); 3139 source_window_show_msg(srcwin, msginfo); 3140 } 3141 3142 void summary_reedit(SummaryView *summaryview) 3143 { 3144 MsgInfo *msginfo; 3145 3146 if (!summaryview->selected) return; 3147 if (!summaryview->folder_item) return; 3148 if (!FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) 3149 return; 3150 3151 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree), 3152 summaryview->selected); 3153 if (!msginfo) return; 3154 3155 compose_reedit(msginfo, FALSE); 3156 } 3157 3158 gboolean summary_is_list(SummaryView *summaryview) 3159 { 3160 return (gtk_notebook_get_current_page( 3161 GTK_NOTEBOOK(summaryview->mainwidget_book)) == 0); 3162 } 3163 3164 void summary_toggle_view(SummaryView *summaryview) 3165 { 3166 if (summary_is_locked(summaryview)) 3167 return; 3168 if (!messageview_is_visible(summaryview->messageview) && 3169 summaryview->selected && summary_is_list(summaryview)) 3170 summary_display_msg(summaryview, 3171 summaryview->selected); 3172 else 3173 main_window_toggle_message_view(summaryview->mainwin); 3174 } 3175 3176 static gboolean summary_search_unread_recursive(GtkCMCTree *ctree, 3177 GtkCMCTreeNode *node) 3178 { 3179 MsgInfo *msginfo; 3180 3181 if (node) { 3182 msginfo = gtk_cmctree_node_get_row_data(ctree, node); 3183 if (msginfo && MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) 3184 return TRUE; 3185 node = GTK_CMCTREE_ROW(node)->children; 3186 } else 3187 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 3188 3189 while (node) { 3190 if (summary_search_unread_recursive(ctree, node) == TRUE) 3191 return TRUE; 3192 node = GTK_CMCTREE_ROW(node)->sibling; 3193 } 3194 3195 return FALSE; 3196 } 3197 3198 static gboolean summary_have_unread_children(SummaryView *summaryview, 3199 GtkCMCTreeNode *node) 3200 { 3201 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3202 3203 if (!node) return FALSE; 3204 3205 node = GTK_CMCTREE_ROW(node)->children; 3206 3207 while (node) { 3208 if (summary_search_unread_recursive(ctree, node) == TRUE) 3209 return TRUE; 3210 node = GTK_CMCTREE_ROW(node)->sibling; 3211 } 3212 return FALSE; 3213 } 3214 3215 static void summary_set_row_marks(SummaryView *summaryview, GtkCMCTreeNode *row) 3216 { 3217 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3218 GtkStyle *style = NULL; 3219 MsgInfo *msginfo; 3220 MsgFlags flags; 3221 gint *col_pos = summaryview->col_pos; 3222 3223 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3224 if (!msginfo) return; 3225 3226 flags = msginfo->flags; 3227 3228 gtk_cmctree_node_set_foreground(ctree, row, NULL); 3229 3230 /* set new/unread column */ 3231 if (MSG_IS_IGNORE_THREAD(flags)) { 3232 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3233 ignorethreadxpm); 3234 } else if (MSG_IS_WATCH_THREAD(flags)) { 3235 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3236 watchthreadxpm); 3237 } else if (MSG_IS_SPAM(flags)) { 3238 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3239 spamxpm); 3240 } else if (MSG_IS_NEW(flags)) { 3241 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3242 newxpm); 3243 } else if (MSG_IS_UNREAD(flags)) { 3244 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3245 unreadxpm); 3246 } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) { 3247 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3248 repliedandforwardedxpm); 3249 } else if (MSG_IS_REPLIED(flags)) { 3250 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3251 repliedxpm); 3252 } else if (MSG_IS_FORWARDED(flags)) { 3253 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_STATUS], 3254 forwardedxpm); 3255 } else { 3256 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_STATUS], 3257 ""); 3258 } 3259 3260 if ((((prefs_common.bold_unread && MSG_IS_UNREAD(flags)) || 3261 (prefs_common.bold_marked && MSG_IS_MARKED(flags))) && 3262 !MSG_IS_IGNORE_THREAD(flags)) || 3263 (!GTK_CMCTREE_ROW(row)->expanded && 3264 GTK_CMCTREE_ROW(row)->children && 3265 summary_have_unread_children(summaryview, row))) 3266 style = bold_style; 3267 3268 /* set mark column */ 3269 if (MSG_IS_DELETED(flags)) { 3270 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK], 3271 deletedxpm); 3272 if (style) 3273 style = bold_style; 3274 gtk_cmctree_node_set_foreground 3275 (ctree, row, &summaryview->color_dim); 3276 } else if (MSG_IS_MARKED(flags)) { 3277 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK], 3278 markxpm); 3279 } else if (MSG_IS_MOVE(flags)) { 3280 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK], 3281 movedxpm); 3282 if (!msginfo->to_folder || 3283 !folder_has_parent_of_type(msginfo->to_folder, F_TRASH)) { 3284 if (style) 3285 style = bold_style; 3286 gtk_cmctree_node_set_foreground 3287 (ctree, row, &summaryview->color_marked); 3288 } else { 3289 if (style) 3290 style = bold_style; 3291 gtk_cmctree_node_set_foreground 3292 (ctree, row, &summaryview->color_dim); 3293 } 3294 } else if (MSG_IS_COPY(flags)) { 3295 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MARK], 3296 copiedxpm); 3297 if (style) 3298 style = bold_style; 3299 gtk_cmctree_node_set_foreground 3300 (ctree, row, &summaryview->color_marked); 3301 } else { 3302 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MARK], ""); 3303 } 3304 3305 if (MSG_IS_LOCKED(flags)) { 3306 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_LOCKED], 3307 lockedxpm); 3308 } 3309 else { 3310 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_LOCKED], ""); 3311 } 3312 3313 if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) { 3314 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3315 clipgpgsignedxpm); 3316 } else if (MSG_IS_SIGNED(flags)) { 3317 if (MSG_IS_ENCRYPTED(flags)) { 3318 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3319 keysignxpm); 3320 } else { 3321 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3322 gpgsignedxpm); 3323 } 3324 } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) { 3325 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3326 clipkeyxpm); 3327 } else if (MSG_IS_ENCRYPTED(flags)) { 3328 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3329 keyxpm); 3330 } else if (MSG_IS_WITH_ATTACHMENT(flags)) { 3331 gtk_cmctree_node_set_pixbuf(ctree, row, col_pos[S_COL_MIME], 3332 clipxpm); 3333 } else { 3334 gtk_cmctree_node_set_text(ctree, row, col_pos[S_COL_MIME], ""); 3335 } 3336 3337 gtk_cmctree_node_set_row_style(ctree, row, style); 3338 } 3339 3340 static void summary_mark_row(SummaryView *summaryview, GtkCMCTreeNode *row) 3341 { 3342 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3343 MsgInfo *msginfo; 3344 3345 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3346 cm_return_if_fail(msginfo); 3347 if (MSG_IS_DELETED(msginfo->flags)) 3348 summaryview->deleted--; 3349 if (MSG_IS_MOVE(msginfo->flags)) 3350 summaryview->moved--; 3351 if (MSG_IS_COPY(msginfo->flags)) 3352 summaryview->copied--; 3353 3354 procmsg_msginfo_set_to_folder(msginfo, NULL); 3355 summary_msginfo_change_flags(msginfo, MSG_MARKED, 0, MSG_DELETED, 3356 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE); 3357 summary_set_row_marks(summaryview, row); 3358 debug_print("Message %s/%d is marked\n", msginfo->folder->path, msginfo->msgnum); 3359 } 3360 3361 static void summary_lock_row(SummaryView *summaryview, GtkCMCTreeNode *row) 3362 { 3363 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3364 MsgInfo *msginfo; 3365 3366 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3367 cm_return_if_fail(msginfo); 3368 if (MSG_IS_DELETED(msginfo->flags)) 3369 summaryview->deleted--; 3370 if (MSG_IS_MOVE(msginfo->flags)) { 3371 summaryview->moved--; 3372 } 3373 if (MSG_IS_COPY(msginfo->flags)) { 3374 summaryview->copied--; 3375 } 3376 procmsg_msginfo_set_to_folder(msginfo, NULL); 3377 summary_msginfo_change_flags(msginfo, MSG_LOCKED, 0, MSG_DELETED, 3378 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE); 3379 3380 summary_set_row_marks(summaryview, row); 3381 debug_print("Message %d is locked\n", msginfo->msgnum); 3382 } 3383 3384 static void summary_unlock_row(SummaryView *summaryview, GtkCMCTreeNode *row) 3385 { 3386 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3387 MsgInfo *msginfo; 3388 3389 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3390 cm_return_if_fail(msginfo); 3391 if (!MSG_IS_LOCKED(msginfo->flags)) 3392 return; 3393 procmsg_msginfo_set_to_folder(msginfo, NULL); 3394 summary_msginfo_unset_flags(msginfo, MSG_LOCKED, 0); 3395 summary_set_row_marks(summaryview, row); 3396 debug_print("Message %d is unlocked\n", msginfo->msgnum); 3397 } 3398 3399 void summary_mark(SummaryView *summaryview) 3400 { 3401 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3402 GList *cur; 3403 gboolean froze = FALSE; 3404 3405 if (summary_is_locked(summaryview)) 3406 return; 3407 START_LONG_OPERATION(summaryview, FALSE); 3408 folder_item_set_batch(summaryview->folder_item, TRUE); 3409 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3410 summary_mark_row(summaryview, GTK_CMCTREE_NODE(cur->data)); 3411 folder_item_set_batch(summaryview->folder_item, FALSE); 3412 END_LONG_OPERATION(summaryview); 3413 3414 summary_status_show(summaryview); 3415 } 3416 3417 static void summary_mark_row_as_read(SummaryView *summaryview, 3418 GtkCMCTreeNode *row) 3419 { 3420 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3421 MsgInfo *msginfo; 3422 3423 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3424 cm_return_if_fail(msginfo); 3425 3426 if(!(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))) 3427 return; 3428 3429 summary_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0); 3430 summary_set_row_marks(summaryview, row); 3431 debug_print("Message %d is marked as read\n", 3432 msginfo->msgnum); 3433 } 3434 3435 static void summary_mark_row_as_unread(SummaryView *summaryview, 3436 GtkCMCTreeNode *row) 3437 { 3438 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3439 MsgInfo *msginfo; 3440 3441 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3442 cm_return_if_fail(msginfo); 3443 3444 if(MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)) 3445 return; 3446 3447 summary_msginfo_set_flags(msginfo, MSG_UNREAD, 0); 3448 summary_set_row_marks(summaryview, row); 3449 debug_print("Message %d is marked as unread\n", 3450 msginfo->msgnum); 3451 } 3452 3453 void summary_mark_as_read(SummaryView *summaryview) 3454 { 3455 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3456 GList *cur; 3457 gboolean froze = FALSE; 3458 3459 if (summary_is_locked(summaryview)) 3460 return; 3461 3462 if (!summaryview->folder_item) 3463 return; 3464 3465 if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) && 3466 summaryview->folder_item->total_msgs > 1) && 3467 !summary_mark_all_read_confirm(TRUE)) 3468 return; 3469 3470 START_LONG_OPERATION(summaryview, FALSE); 3471 folder_item_set_batch(summaryview->folder_item, TRUE); 3472 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3473 summary_mark_row_as_read(summaryview, 3474 GTK_CMCTREE_NODE(cur->data)); 3475 folder_item_set_batch(summaryview->folder_item, FALSE); 3476 END_LONG_OPERATION(summaryview); 3477 3478 summary_status_show(summaryview); 3479 } 3480 3481 void summary_mark_as_unread(SummaryView *summaryview) 3482 { 3483 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3484 GList *cur; 3485 gboolean froze = FALSE; 3486 3487 if (summary_is_locked(summaryview)) 3488 return; 3489 3490 if ((summaryview->folder_item->total_msgs == (gint)g_list_length(GTK_CMCLIST(ctree)->selection) && 3491 summaryview->folder_item->total_msgs > 1) && 3492 !summary_mark_all_unread_confirm(TRUE)) 3493 return; 3494 3495 START_LONG_OPERATION(summaryview, FALSE); 3496 folder_item_set_batch(summaryview->folder_item, TRUE); 3497 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3498 summary_mark_row_as_unread(summaryview, 3499 GTK_CMCTREE_NODE(cur->data)); 3500 folder_item_set_batch(summaryview->folder_item, FALSE); 3501 END_LONG_OPERATION(summaryview); 3502 3503 summary_status_show(summaryview); 3504 } 3505 3506 void summary_msgs_lock(SummaryView *summaryview) 3507 { 3508 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3509 GList *cur; 3510 gboolean froze = FALSE; 3511 3512 if (summary_is_locked(summaryview)) 3513 return; 3514 START_LONG_OPERATION(summaryview, FALSE); 3515 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3516 summary_lock_row(summaryview, 3517 GTK_CMCTREE_NODE(cur->data)); 3518 END_LONG_OPERATION(summaryview); 3519 3520 summary_status_show(summaryview); 3521 } 3522 3523 void summary_msgs_unlock(SummaryView *summaryview) 3524 { 3525 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3526 GList *cur; 3527 gboolean froze = FALSE; 3528 3529 if (summary_is_locked(summaryview)) 3530 return; 3531 START_LONG_OPERATION(summaryview, FALSE); 3532 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3533 summary_unlock_row(summaryview, 3534 GTK_CMCTREE_NODE(cur->data)); 3535 END_LONG_OPERATION(summaryview); 3536 3537 summary_status_show(summaryview); 3538 } 3539 3540 static gboolean summary_mark_all_read_confirm(gboolean ask_if_needed) 3541 { 3542 /* ask_if_needed is FALSE when user-asking is performed by caller, 3543 commonly when the caller is a mark-as-read-recursive func */ 3544 if (ask_if_needed && prefs_common.ask_mark_all_read) { 3545 AlertValue val = alertpanel_full(_("Mark all as read"), 3546 _("Do you really want to mark all mails in this folder as read?"), 3547 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST, 3548 TRUE, NULL, ALERT_QUESTION); 3549 3550 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) 3551 return FALSE; 3552 else if (val & G_ALERTDISABLE) 3553 prefs_common.ask_mark_all_read = FALSE; 3554 } 3555 return TRUE; 3556 } 3557 3558 void summary_mark_all_read(SummaryView *summaryview, gboolean ask_if_needed) 3559 { 3560 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3561 GtkCMCTreeNode *node; 3562 gboolean froze = FALSE; 3563 3564 if (summary_is_locked(summaryview)) 3565 return; 3566 3567 if (!summary_mark_all_read_confirm(ask_if_needed)) 3568 return; 3569 3570 START_LONG_OPERATION(summaryview, TRUE); 3571 folder_item_set_batch(summaryview->folder_item, TRUE); 3572 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL; 3573 node = gtkut_ctree_node_next(ctree, node)) 3574 summary_mark_row_as_read(summaryview, node); 3575 folder_item_set_batch(summaryview->folder_item, FALSE); 3576 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL; 3577 node = gtkut_ctree_node_next(ctree, node)) { 3578 if (!GTK_CMCTREE_ROW(node)->expanded) 3579 summary_set_row_marks(summaryview, node); 3580 } 3581 END_LONG_OPERATION(summaryview); 3582 3583 summary_status_show(summaryview); 3584 } 3585 3586 static gboolean summary_mark_all_unread_confirm(gboolean ask_if_needed) 3587 { 3588 /* ask_if_needed is FALSE when user-asking is performed by caller, 3589 commonly when the caller is a mark-as-unread-recursive func */ 3590 if (ask_if_needed && prefs_common.ask_mark_all_read) { 3591 AlertValue val = alertpanel_full(_("Mark all as unread"), 3592 _("Do you really want to mark all mails in this folder as unread?"), 3593 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, ALERTFOCUS_FIRST, 3594 TRUE, NULL, ALERT_QUESTION); 3595 3596 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) 3597 return FALSE; 3598 else if (val & G_ALERTDISABLE) 3599 prefs_common.ask_mark_all_read = FALSE; 3600 } 3601 return TRUE; 3602 } 3603 3604 void summary_mark_all_unread(SummaryView *summaryview, gboolean ask_if_needed) 3605 { 3606 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3607 GtkCMCTreeNode *node; 3608 gboolean froze = FALSE; 3609 3610 if (summary_is_locked(summaryview)) 3611 return; 3612 3613 if (!summary_mark_all_unread_confirm(ask_if_needed)) 3614 return; 3615 3616 START_LONG_OPERATION(summaryview, TRUE); 3617 folder_item_set_batch(summaryview->folder_item, TRUE); 3618 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL; 3619 node = gtkut_ctree_node_next(ctree, node)) 3620 summary_mark_row_as_unread(summaryview, node); 3621 folder_item_set_batch(summaryview->folder_item, FALSE); 3622 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); node != NULL; 3623 node = gtkut_ctree_node_next(ctree, node)) { 3624 if (!GTK_CMCTREE_ROW(node)->expanded) 3625 summary_set_row_marks(summaryview, node); 3626 } 3627 END_LONG_OPERATION(summaryview); 3628 3629 summary_status_show(summaryview); 3630 } 3631 3632 static gboolean check_permission(SummaryView *summaryview, MsgInfo * msginfo) 3633 { 3634 GList * cur; 3635 gboolean found; 3636 3637 switch (FOLDER_TYPE(summaryview->folder_item->folder)) { 3638 3639 case F_NEWS: 3640 3641 /* 3642 security : checks if one the accounts correspond to 3643 the author of the post 3644 */ 3645 3646 found = FALSE; 3647 for(cur = account_get_list() ; cur != NULL ; cur = cur->next) { 3648 PrefsAccount * account; 3649 gchar * from_name; 3650 3651 account = cur->data; 3652 if (account->name && *account->name) 3653 from_name = 3654 g_strdup_printf("%s <%s>", 3655 account->name, 3656 account->address); 3657 else 3658 from_name = 3659 g_strdup_printf("%s", 3660 account->address); 3661 3662 if (g_utf8_collate(from_name, msginfo->from) == 0) { 3663 g_free(from_name); 3664 found = TRUE; 3665 break; 3666 } 3667 g_free(from_name); 3668 } 3669 3670 if (!found) { 3671 alertpanel_error(_("You're not the author of the article.")); 3672 } 3673 3674 return found; 3675 3676 default: 3677 return TRUE; 3678 } 3679 } 3680 3681 static void summary_delete_row(SummaryView *summaryview, GtkCMCTreeNode *row) 3682 { 3683 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3684 MsgInfo *msginfo; 3685 3686 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3687 cm_return_if_fail(msginfo); 3688 3689 if (MSG_IS_LOCKED(msginfo->flags)) return; 3690 3691 if (MSG_IS_DELETED(msginfo->flags)) return; 3692 3693 if (MSG_IS_MOVE(msginfo->flags)) 3694 summaryview->moved--; 3695 if (MSG_IS_COPY(msginfo->flags)) 3696 summaryview->copied--; 3697 3698 procmsg_msginfo_set_to_folder(msginfo, NULL); 3699 summary_msginfo_change_flags(msginfo, MSG_DELETED, 0, MSG_MARKED, 3700 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE); 3701 summaryview->deleted++; 3702 3703 if (!prefs_common.immediate_exec && 3704 !folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) { 3705 summary_set_row_marks(summaryview, row); 3706 } 3707 debug_print("Message %s/%d is set to delete\n", 3708 msginfo->folder->path, msginfo->msgnum); 3709 } 3710 3711 void summary_cancel(SummaryView *summaryview) 3712 { 3713 MsgInfo * msginfo; 3714 3715 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree), 3716 summaryview->selected); 3717 if (!msginfo) return; 3718 3719 if (!check_permission(summaryview, msginfo)) 3720 return; 3721 3722 if (summary_is_locked(summaryview)) return; 3723 3724 summary_lock(summaryview); 3725 3726 summary_freeze(summaryview); 3727 3728 summary_update_status(summaryview); 3729 summary_status_show(summaryview); 3730 3731 summary_thaw(summaryview); 3732 3733 summary_unlock(summaryview); 3734 } 3735 3736 void summary_delete(SummaryView *summaryview) 3737 { 3738 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3739 FolderItem *item = summaryview->folder_item; 3740 GList *cur; 3741 GtkCMCTreeNode *sel_last = NULL; 3742 GtkCMCTreeNode *node; 3743 AlertValue aval; 3744 gboolean froze = FALSE; 3745 3746 if (!item) return; 3747 3748 if (summary_is_locked(summaryview)) return; 3749 3750 if (!summaryview->folder_item) return; 3751 3752 START_LONG_OPERATION(summaryview, FALSE); 3753 3754 if (!prefs_common.live_dangerously) { 3755 guint num = g_list_length(GTK_CMCLIST(summaryview->ctree)->selection); 3756 char buf[BUFSIZ]; 3757 snprintf(buf, sizeof(buf), "Delete the selected %d messages?", num); 3758 aval = alertpanel("Delete messages", buf, 3759 NULL, "_Cancel", NULL, "_Delete", NULL, NULL, ALERTFOCUS_SECOND); 3760 if (aval != G_ALERTALTERNATE) { 3761 END_LONG_OPERATION(summaryview); 3762 return; 3763 } 3764 } 3765 3766 main_window_cursor_wait(summaryview->mainwin); 3767 3768 /* next code sets current row focus right. We need to find a row 3769 * that is not deleted. */ 3770 folder_item_set_batch(summaryview->folder_item, TRUE); 3771 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) { 3772 sel_last = GTK_CMCTREE_NODE(cur->data); 3773 summary_delete_row(summaryview, sel_last); 3774 } 3775 folder_item_set_batch(summaryview->folder_item, FALSE); 3776 END_LONG_OPERATION(summaryview); 3777 3778 if (summaryview->sort_type == SORT_ASCENDING) { 3779 node = summary_find_next_msg(summaryview, sel_last, TRUE); 3780 if (!node || prefs_common.next_on_delete == FALSE) 3781 node = summary_find_prev_msg(summaryview, sel_last,TRUE); 3782 } else { 3783 node = summary_find_prev_msg(summaryview, sel_last,TRUE); 3784 if (!node || prefs_common.next_on_delete == FALSE) 3785 node = summary_find_next_msg(summaryview, sel_last,TRUE); 3786 } 3787 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE); 3788 3789 if (prefs_common.immediate_exec || folder_has_parent_of_type(item, F_TRASH)) { 3790 summary_execute(summaryview); 3791 /* after deleting, the anchor may be at an invalid row 3792 * so reset it to the node we found earlier */ 3793 gtk_sctree_set_anchor_row(GTK_SCTREE(ctree), node); 3794 } else 3795 summary_status_show(summaryview); 3796 3797 3798 main_window_cursor_normal(summaryview->mainwin); 3799 } 3800 3801 void summary_delete_trash(SummaryView *summaryview) 3802 { 3803 FolderItem *to_folder = NULL; 3804 PrefsAccount *ac; 3805 if (!summaryview->folder_item || 3806 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return; 3807 3808 if (NULL != (ac = account_find_from_item(summaryview->folder_item))) 3809 to_folder = account_get_special_folder(ac, F_TRASH); 3810 3811 if (to_folder == NULL) 3812 to_folder = summaryview->folder_item->folder->trash; 3813 3814 if (to_folder == NULL || to_folder == summaryview->folder_item 3815 || folder_has_parent_of_type(summaryview->folder_item, F_TRASH)) 3816 summary_delete(summaryview); 3817 else 3818 summary_move_selected_to(summaryview, to_folder); 3819 } 3820 3821 3822 static void summary_unmark_row(SummaryView *summaryview, GtkCMCTreeNode *row) 3823 { 3824 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3825 MsgInfo *msginfo; 3826 3827 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3828 cm_return_if_fail(msginfo); 3829 if (MSG_IS_DELETED(msginfo->flags)) 3830 summaryview->deleted--; 3831 if (MSG_IS_MOVE(msginfo->flags)) 3832 summaryview->moved--; 3833 if (MSG_IS_COPY(msginfo->flags)) 3834 summaryview->copied--; 3835 3836 procmsg_msginfo_set_to_folder(msginfo, NULL); 3837 summary_msginfo_unset_flags(msginfo, MSG_MARKED | MSG_DELETED, 3838 MSG_MOVE | MSG_COPY | MSG_MOVE_DONE); 3839 summary_set_row_marks(summaryview, row); 3840 3841 debug_print("Message %s/%d is unmarked\n", 3842 msginfo->folder->path, msginfo->msgnum); 3843 } 3844 3845 void summary_unmark(SummaryView *summaryview) 3846 { 3847 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3848 GList *cur; 3849 gboolean froze = FALSE; 3850 3851 if (summary_is_locked(summaryview)) 3852 return; 3853 START_LONG_OPERATION(summaryview, FALSE); 3854 folder_item_set_batch(summaryview->folder_item, TRUE); 3855 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 3856 summary_unmark_row(summaryview, GTK_CMCTREE_NODE(cur->data)); 3857 folder_item_set_batch(summaryview->folder_item, FALSE); 3858 END_LONG_OPERATION(summaryview); 3859 3860 summary_status_show(summaryview); 3861 } 3862 3863 static void summary_move_row_to(SummaryView *summaryview, GtkCMCTreeNode *row, 3864 FolderItem *to_folder) 3865 { 3866 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3867 MsgInfo *msginfo; 3868 3869 cm_return_if_fail(to_folder != NULL); 3870 3871 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3872 cm_return_if_fail(msginfo); 3873 if (MSG_IS_LOCKED(msginfo->flags)) 3874 return; 3875 3876 procmsg_msginfo_set_to_folder(msginfo, to_folder); 3877 if (MSG_IS_DELETED(msginfo->flags)) 3878 summaryview->deleted--; 3879 if (MSG_IS_COPY(msginfo->flags)) { 3880 summaryview->copied--; 3881 } 3882 if (!MSG_IS_MOVE(msginfo->flags)) { 3883 summary_msginfo_change_flags(msginfo, 0, MSG_MOVE, MSG_DELETED, 3884 MSG_COPY | MSG_MOVE_DONE); 3885 summaryview->moved++; 3886 } else { 3887 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_COPY); 3888 } 3889 3890 if (!prefs_common.immediate_exec) { 3891 summary_set_row_marks(summaryview, row); 3892 } 3893 3894 debug_print("Message %d is set to move to %s\n", 3895 msginfo->msgnum, to_folder->path); 3896 } 3897 3898 void summary_move_selected_to(SummaryView *summaryview, FolderItem *to_folder) 3899 { 3900 GList *cur; 3901 GtkCMCTreeNode *sel_last = NULL; 3902 gboolean froze = FALSE; 3903 3904 if (!to_folder) return; 3905 if (!summaryview->folder_item || 3906 FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) return; 3907 3908 if (summary_is_locked(summaryview)) return; 3909 3910 if (summaryview->folder_item == to_folder) { 3911 alertpanel_error(_("Destination is same as current folder.")); 3912 return; 3913 } 3914 3915 if (to_folder->no_select) { 3916 alertpanel_error(_("The destination folder can only be used to " 3917 "store subfolders.")); 3918 return; 3919 } 3920 3921 START_LONG_OPERATION(summaryview, FALSE); 3922 3923 for (cur = GTK_CMCLIST(summaryview->ctree)->selection; 3924 cur != NULL && cur->data != NULL; cur = cur->next) { 3925 sel_last = GTK_CMCTREE_NODE(cur->data); 3926 summary_move_row_to 3927 (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder); 3928 } 3929 END_LONG_OPERATION(summaryview); 3930 3931 if (prefs_common.immediate_exec) { 3932 summary_execute(summaryview); 3933 } else { 3934 GtkCMCTreeNode *node = NULL; 3935 if (summaryview->sort_type == SORT_ASCENDING) { 3936 node = summary_find_next_msg(summaryview, sel_last,TRUE); 3937 if (!node || prefs_common.next_on_delete == FALSE) 3938 node = summary_find_prev_msg(summaryview, sel_last,TRUE); 3939 } else { 3940 node = summary_find_prev_msg(summaryview, sel_last,TRUE); 3941 if (!node || prefs_common.next_on_delete == FALSE) 3942 node = summary_find_next_msg(summaryview, sel_last,TRUE); 3943 } 3944 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE); 3945 summary_status_show(summaryview); 3946 } 3947 3948 if (!summaryview->selected) { /* this was the last message */ 3949 GtkCMCTreeNode *node = gtk_cmctree_node_nth (GTK_CMCTREE(summaryview->ctree), 3950 GTK_CMCLIST(summaryview->ctree)->rows - 1); 3951 if (node) 3952 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DELETEMOVE); 3953 } 3954 3955 } 3956 3957 void summary_move_to(SummaryView *summaryview) 3958 { 3959 if (!summaryview->folder_item) return; 3960 3961 char *title = "Select destination folder"; 3962 FolderItem *dst = foldersel_folder_sel(NULL, FOLDER_SEL_MOVE, NULL, FALSE, title); 3963 summary_move_selected_to(summaryview, dst); 3964 } 3965 3966 static void summary_copy_row_to(SummaryView *summaryview, GtkCMCTreeNode *row, 3967 FolderItem *to_folder) 3968 { 3969 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 3970 MsgInfo *msginfo; 3971 3972 cm_return_if_fail(to_folder != NULL); 3973 3974 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 3975 cm_return_if_fail(msginfo); 3976 procmsg_msginfo_set_to_folder(msginfo, to_folder); 3977 if (MSG_IS_DELETED(msginfo->flags)) 3978 summaryview->deleted--; 3979 if (MSG_IS_MOVE(msginfo->flags)) { 3980 summaryview->moved--; 3981 } 3982 3983 if (!MSG_IS_COPY(msginfo->flags)) { 3984 summary_msginfo_change_flags(msginfo, 0, MSG_COPY, MSG_DELETED, 3985 MSG_MOVE | MSG_MOVE_DONE); 3986 summaryview->copied++; 3987 } else { 3988 summary_msginfo_unset_flags(msginfo, MSG_DELETED, MSG_MOVE); 3989 } 3990 if (!prefs_common.immediate_exec) { 3991 summary_set_row_marks(summaryview, row); 3992 } 3993 3994 debug_print("Message %d is set to copy to %s\n", 3995 msginfo->msgnum, to_folder->path); 3996 } 3997 3998 void summary_copy_selected_to(SummaryView *summaryview, FolderItem *to_folder) 3999 { 4000 GList *cur; 4001 gboolean froze = FALSE; 4002 4003 if (!to_folder) return; 4004 if (!summaryview->folder_item) return; 4005 4006 if (summary_is_locked(summaryview)) return; 4007 4008 if (summaryview->folder_item == to_folder) { 4009 alertpanel_error("Destination to copy is same as current folder."); 4010 return; 4011 } 4012 4013 if (to_folder->no_select) { 4014 alertpanel_error("The destination folder can only be used to store subfolders."); 4015 return; 4016 } 4017 4018 START_LONG_OPERATION(summaryview, FALSE); 4019 4020 for (cur = GTK_CMCLIST(summaryview->ctree)->selection; 4021 cur != NULL && cur->data != NULL; cur = cur->next) 4022 summary_copy_row_to 4023 (summaryview, GTK_CMCTREE_NODE(cur->data), to_folder); 4024 4025 END_LONG_OPERATION(summaryview); 4026 4027 if (prefs_common.immediate_exec) { 4028 summary_execute(summaryview); 4029 return; 4030 } 4031 summary_status_show(summaryview); 4032 } 4033 4034 void summary_copy_to(SummaryView *summaryview) 4035 { 4036 if (!summaryview->folder_item) return; 4037 4038 char *title = "Select destination folder"; 4039 FolderItem *dst = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL, FALSE, title); 4040 summary_copy_selected_to(summaryview, dst); 4041 } 4042 4043 void summary_add_address(SummaryView *summaryview) 4044 { 4045 MsgInfo *msginfo, *full_msginfo; 4046 gchar *from; 4047 4048 msginfo = gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree), 4049 summaryview->selected); 4050 if (!msginfo || !msginfo->from) 4051 return; 4052 4053 Xstrdup_a(from, msginfo->from, return); 4054 eliminate_address_comment(from); 4055 extract_address(from); 4056 4057 full_msginfo = procmsg_msginfo_get_full_info(msginfo); 4058 4059 procmsg_msginfo_free(&full_msginfo); 4060 4061 addressbook_add_contact(msginfo->fromname, from, NULL, NULL); 4062 } 4063 4064 void summary_select_all(SummaryView *summaryview) 4065 { 4066 GtkCMCTreeNode *node; 4067 4068 if (!summaryview->folder_item) return; 4069 4070 if (GTK_CMCLIST(summaryview->ctree)->focus_row < 0) { 4071 /* If no row is selected, select (but do not open) the first 4072 * row, to get summaryview into correct state for selecting all. */ 4073 debug_print("summary_select_all: no row selected, selecting first one\n"); 4074 if (GTK_CMCLIST(summaryview->ctree)->row_list != NULL) { 4075 node = gtk_cmctree_node_nth(GTK_CMCTREE(summaryview->ctree), 0); 4076 summary_select_node(summaryview, node, FALSE); 4077 } 4078 } 4079 4080 /* Now select all rows while locking the summaryview for 4081 * faster performance. */ 4082 summary_lock(summaryview); 4083 gtk_cmclist_select_all(GTK_CMCLIST(summaryview->ctree)); 4084 summary_unlock(summaryview); 4085 4086 summary_status_show(summaryview); 4087 } 4088 4089 void summary_unselect_all(SummaryView *summaryview) 4090 { 4091 summary_lock(summaryview); 4092 gtk_sctree_unselect_all(GTK_SCTREE(summaryview->ctree)); 4093 summary_unlock(summaryview); 4094 summary_status_show(summaryview); 4095 } 4096 4097 void summary_select_thread(SummaryView *summaryview, gboolean delete_thread, 4098 gboolean trash_thread) 4099 { 4100 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4101 GtkCMCTreeNode *node = NULL; 4102 gboolean froze = FALSE; 4103 GList *cur = NULL; 4104 GList *copy = NULL; 4105 if (!GTK_CMCLIST(summaryview->ctree)->selection) 4106 return; 4107 4108 4109 START_LONG_OPERATION(summaryview, FALSE); 4110 copy = g_list_copy(GTK_CMCLIST(summaryview->ctree)->selection); 4111 for (cur = copy; cur != NULL && cur->data != NULL; 4112 cur = cur->next) { 4113 node = GTK_CMCTREE_NODE(cur->data); 4114 if (!node) 4115 continue; 4116 while (GTK_CMCTREE_ROW(node)->parent != NULL) 4117 node = GTK_CMCTREE_ROW(node)->parent; 4118 4119 gtk_cmctree_select_recursive(ctree, node); 4120 } 4121 g_list_free(copy); 4122 END_LONG_OPERATION(summaryview); 4123 4124 if (trash_thread) { 4125 if (FOLDER_TYPE(summaryview->folder_item->folder) == F_NEWS) 4126 summary_delete(summaryview); 4127 else 4128 summary_delete_trash(summaryview); 4129 } else if (delete_thread) 4130 summary_delete(summaryview); 4131 4132 summary_status_show(summaryview); 4133 } 4134 4135 void summary_save_as(SummaryView *summaryview) 4136 { 4137 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4138 MsgInfo *msginfo; 4139 gchar *filename = NULL; 4140 gchar *src, *dest; 4141 gchar *tmp; 4142 gchar *filedir = NULL; 4143 gchar *converted_filename = NULL; 4144 gchar * filepath = NULL; 4145 4146 AlertValue aval = 0; 4147 4148 if (!summaryview->selected) return; 4149 msginfo = gtk_cmctree_node_get_row_data(ctree, summaryview->selected); 4150 if (!msginfo) return; 4151 4152 if (msginfo->subject) { 4153 Xstrdup_a(filename, msginfo->subject, return); 4154 subst_for_filename(filename); 4155 } 4156 4157 manage_window_focus_in(summaryview->window, NULL, NULL); 4158 4159 if (filename && !g_utf8_validate(filename, -1, NULL)) { 4160 converted_filename = conv_codeset_strdup(filename, 4161 conv_get_locale_charset_str(), 4162 CS_UTF_8); 4163 if (!converted_filename) { 4164 g_warning("summary_save_as(): failed to convert character set"); 4165 } else { 4166 filename = converted_filename; 4167 } 4168 } 4169 if (!filename) 4170 return; 4171 4172 if (prefs_common.attach_save_dir && *prefs_common.attach_save_dir) 4173 filepath = g_strconcat(prefs_common.attach_save_dir, G_DIR_SEPARATOR_S, filename, NULL); 4174 dest = filesel_select_file_save(_("Save as"), filepath ? filepath : filename); 4175 if (filepath) 4176 g_free(filepath); 4177 if (converted_filename) 4178 g_free(converted_filename); 4179 if (!dest) 4180 return; 4181 4182 if (is_file_exist(dest)) { 4183 aval = alertpanel("Append or Overwrite?", "", 4184 NULL, "_Append", NULL, "_Overwrite", 4185 NULL, "_Cancel", ALERTFOCUS_FIRST); 4186 if (aval != 0 && aval != 1) 4187 return; 4188 } 4189 4190 src = procmsg_get_message_file(msginfo); 4191 tmp = g_path_get_basename(dest); 4192 4193 if ( aval==0 ) { /* append */ 4194 if (append_file(src, dest, TRUE) < 0) 4195 alertpanel_error("Couldn't save the file '%s'.", tmp); 4196 } else { /* overwrite */ 4197 if (copy_file(src, dest, TRUE) < 0) 4198 alertpanel_error("Couldn't save the file '%s'.", tmp); 4199 } 4200 g_free(src); 4201 4202 /* 4203 * If two or more msgs are selected, 4204 * append them to the output file. 4205 */ 4206 if (GTK_CMCLIST(ctree)->selection->next) { 4207 GList *item; 4208 for (item = GTK_CMCLIST(ctree)->selection->next; item != NULL; item=item->next) { 4209 msginfo = gtk_cmctree_node_get_row_data(ctree, GTK_CMCTREE_NODE(item->data)); 4210 if (!msginfo) break; 4211 src = procmsg_get_message_file(msginfo); 4212 if (append_file(src, dest, TRUE) < 0) { 4213 alertpanel_error("Couldn't save the file '%s'.", tmp); 4214 } 4215 g_free(src); 4216 } 4217 } 4218 4219 filedir = g_path_get_dirname(dest); 4220 if (filedir) { 4221 if (strcmp(filedir, ".")) { 4222 g_free(prefs_common.attach_save_dir); 4223 prefs_common.attach_save_dir = g_filename_to_utf8(filedir, -1, NULL, NULL, NULL); 4224 } 4225 g_free(filedir); 4226 } 4227 4228 g_free(dest); 4229 g_free(tmp); 4230 } 4231 4232 gboolean summary_execute(SummaryView *summaryview) 4233 { 4234 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4235 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 4236 GtkCMCTreeNode *node, *next; 4237 GtkCMCTreeNode *new_selected = NULL; 4238 gint move_val = -1; 4239 4240 if (!summaryview->folder_item) return FALSE; 4241 4242 if (summary_is_locked(summaryview)) return FALSE; 4243 summary_lock(summaryview); 4244 4245 summary_freeze(summaryview); 4246 4247 main_window_cursor_wait(summaryview->mainwin); 4248 4249 if (summaryview->threaded) 4250 summary_unthread_for_exec(summaryview); 4251 4252 folder_item_update_freeze(); 4253 move_val = summary_execute_move(summaryview); 4254 summary_execute_copy(summaryview); 4255 summary_execute_delete(summaryview); 4256 4257 node = GTK_CMCTREE_NODE(clist->row_list); 4258 for (; node != NULL; node = next) { 4259 next = gtkut_ctree_node_next(ctree, node); 4260 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue; 4261 4262 if (node == summaryview->displayed) { 4263 messageview_clear(summaryview->messageview); 4264 summary_cancel_mark_read_timeout(summaryview); 4265 summaryview->displayed = NULL; 4266 } 4267 if (GTK_CMCTREE_ROW(node)->children != NULL) { 4268 next = NULL; 4269 if (GTK_CMCTREE_ROW(node)->sibling) { 4270 next = GTK_CMCTREE_ROW(node)->sibling; 4271 } else { 4272 GtkCMCTreeNode *parent = NULL; 4273 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL; 4274 parent = GTK_CMCTREE_ROW(parent)->parent) { 4275 if (GTK_CMCTREE_ROW(parent)->sibling) { 4276 next = GTK_CMCTREE_ROW(parent)->sibling; 4277 } 4278 } 4279 } 4280 } 4281 4282 if (!new_selected && 4283 gtkut_ctree_node_is_selected(ctree, node)) { 4284 summary_unselect_all(summaryview); 4285 if (summaryview->sort_type == SORT_ASCENDING) { 4286 new_selected = summary_find_next_msg(summaryview, node,TRUE); 4287 if (!new_selected || prefs_common.next_on_delete == FALSE) 4288 new_selected = summary_find_prev_msg(summaryview, node,TRUE); 4289 } else { 4290 new_selected = summary_find_prev_msg(summaryview, node,TRUE); 4291 if (!new_selected || prefs_common.next_on_delete == FALSE) 4292 new_selected = summary_find_next_msg(summaryview, node,TRUE); 4293 } 4294 } 4295 4296 gtk_sctree_remove_node((GtkSCTree *)ctree, node); 4297 } 4298 4299 folder_item_update_thaw(); 4300 4301 if (new_selected) { 4302 summary_unlock(summaryview); 4303 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE); 4304 summary_lock(summaryview); 4305 } 4306 4307 if (summaryview->threaded) { 4308 summary_thread_build(summaryview); 4309 summary_thread_init(summaryview); 4310 } 4311 4312 summary_thaw(summaryview); 4313 4314 summaryview->selected = clist->selection ? 4315 GTK_CMCTREE_NODE(clist->selection->data) : NULL; 4316 4317 if (!GTK_CMCLIST(summaryview->ctree)->row_list) { 4318 menu_set_insensitive_all 4319 (GTK_MENU_SHELL(summaryview->popupmenu)); 4320 folderview_grab_focus(summaryview->folderview); 4321 } else { 4322 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE); 4323 gtk_widget_grab_focus(summaryview->ctree); 4324 } 4325 summary_update_status(summaryview); 4326 summary_status_show(summaryview); 4327 4328 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 4329 4330 summary_unlock(summaryview); 4331 4332 main_window_cursor_normal(summaryview->mainwin); 4333 4334 if (move_val < 0) 4335 summary_show(summaryview, summaryview->folder_item, FALSE); 4336 return TRUE; 4337 } 4338 4339 gboolean summary_expunge(SummaryView *summaryview) 4340 { 4341 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4342 GtkCMCList *clist = GTK_CMCLIST(summaryview->ctree); 4343 GtkCMCTreeNode *node, *next; 4344 GtkCMCTreeNode *new_selected = NULL; 4345 4346 if (!summaryview->folder_item) return FALSE; 4347 4348 if (summary_is_locked(summaryview)) return FALSE; 4349 4350 summary_lock(summaryview); 4351 4352 summary_freeze(summaryview); 4353 4354 main_window_cursor_wait(summaryview->mainwin); 4355 4356 if (summaryview->threaded) 4357 summary_unthread_for_exec(summaryview); 4358 4359 folder_item_update_freeze(); 4360 summary_execute_expunge(summaryview); 4361 4362 node = GTK_CMCTREE_NODE(clist->row_list); 4363 for (; node != NULL; node = next) { 4364 next = gtkut_ctree_node_next(ctree, node); 4365 if (gtk_cmctree_node_get_row_data(ctree, node) != NULL) continue; 4366 4367 if (node == summaryview->displayed) { 4368 messageview_clear(summaryview->messageview); 4369 summary_cancel_mark_read_timeout(summaryview); 4370 summaryview->displayed = NULL; 4371 } 4372 if (GTK_CMCTREE_ROW(node)->children != NULL) { 4373 next = NULL; 4374 if (GTK_CMCTREE_ROW(node)->sibling) { 4375 next = GTK_CMCTREE_ROW(node)->sibling; 4376 } else { 4377 GtkCMCTreeNode *parent = NULL; 4378 for (parent = GTK_CMCTREE_ROW(node)->parent; parent != NULL; 4379 parent = GTK_CMCTREE_ROW(parent)->parent) { 4380 if (GTK_CMCTREE_ROW(parent)->sibling) { 4381 next = GTK_CMCTREE_ROW(parent)->sibling; 4382 } 4383 } 4384 } 4385 } 4386 4387 if (!new_selected && 4388 gtkut_ctree_node_is_selected(ctree, node)) { 4389 summary_unselect_all(summaryview); 4390 new_selected = summary_find_next_msg(summaryview, node,TRUE); 4391 if (!new_selected) 4392 new_selected = summary_find_prev_msg 4393 (summaryview, node,TRUE); 4394 } 4395 4396 gtk_sctree_remove_node((GtkSCTree *)ctree, node); 4397 } 4398 4399 folder_item_update_thaw(); 4400 4401 if (new_selected) { 4402 summary_unlock(summaryview); 4403 summary_select_node(summaryview, new_selected, OPEN_SELECTED_ON_DELETEMOVE); 4404 summary_lock(summaryview); 4405 } 4406 4407 if (summaryview->threaded) { 4408 summary_thread_build(summaryview); 4409 summary_thread_init(summaryview); 4410 } 4411 4412 summary_thaw(summaryview); 4413 4414 summaryview->selected = clist->selection ? 4415 GTK_CMCTREE_NODE(clist->selection->data) : NULL; 4416 4417 if (!GTK_CMCLIST(summaryview->ctree)->row_list) { 4418 menu_set_insensitive_all 4419 (GTK_MENU_SHELL(summaryview->popupmenu)); 4420 folderview_grab_focus(summaryview->folderview); 4421 } else { 4422 menu_set_sensitive_all(GTK_MENU_SHELL(summaryview->popupmenu), TRUE); 4423 gtk_widget_grab_focus(summaryview->ctree); 4424 } 4425 4426 summary_update_status(summaryview); 4427 summary_status_show(summaryview); 4428 4429 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 4430 4431 summary_unlock(summaryview); 4432 4433 main_window_cursor_normal(summaryview->mainwin); 4434 4435 return TRUE; 4436 } 4437 4438 static gint summary_execute_move(SummaryView *summaryview) 4439 { 4440 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4441 GSList *cur; 4442 gint val = -1; 4443 /* search moving messages and execute */ 4444 4445 gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_move_func, 4446 summaryview); 4447 4448 if (summaryview->mlist) { 4449 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, 4450 summaryview->msginfo_update_callback_id); 4451 val = procmsg_move_messages(summaryview->mlist); 4452 summaryview->msginfo_update_callback_id = 4453 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 4454 summary_update_msg, (gpointer) summaryview); 4455 4456 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) { 4457 procmsg_msginfo_free((MsgInfo **)&(cur->data)); 4458 } 4459 4460 g_slist_free(summaryview->mlist); 4461 summaryview->mlist = NULL; 4462 return val; 4463 } 4464 return 0; 4465 } 4466 4467 static void summary_execute_move_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 4468 gpointer data) 4469 { 4470 SummaryView *summaryview = data; 4471 MsgInfo *msginfo; 4472 4473 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4474 4475 if (msginfo && MSG_IS_MOVE(msginfo->flags) && msginfo->to_folder) { 4476 summaryview->mlist = 4477 g_slist_prepend(summaryview->mlist, msginfo); 4478 gtk_cmctree_node_set_row_data(ctree, node, NULL); 4479 4480 if (msginfo->msgid && *msginfo->msgid && 4481 node == g_hash_table_lookup(summaryview->msgid_table, 4482 msginfo->msgid)) 4483 g_hash_table_remove(summaryview->msgid_table, 4484 msginfo->msgid); 4485 if (prefs_common.thread_by_subject && 4486 msginfo->subject && *msginfo->subject && 4487 node == subject_table_lookup(summaryview->subject_table, 4488 msginfo->subject)) { 4489 subject_table_remove(summaryview->subject_table, 4490 msginfo->subject); 4491 } 4492 } 4493 } 4494 4495 static void summary_execute_copy(SummaryView *summaryview) 4496 { 4497 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4498 4499 /* search copying messages and execute */ 4500 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, 4501 summaryview->msginfo_update_callback_id); 4502 gtk_cmctree_pre_recursive(ctree, NULL, summary_execute_copy_func, 4503 summaryview); 4504 4505 if (summaryview->mlist) { 4506 summaryview->mlist = g_slist_reverse(summaryview->mlist); 4507 procmsg_copy_messages(summaryview->mlist); 4508 4509 g_slist_free(summaryview->mlist); 4510 summaryview->mlist = NULL; 4511 } 4512 summaryview->msginfo_update_callback_id = 4513 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 4514 summary_update_msg, (gpointer) summaryview); 4515 } 4516 4517 static void summary_execute_copy_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 4518 gpointer data) 4519 { 4520 SummaryView *summaryview = data; 4521 MsgInfo *msginfo; 4522 4523 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4524 4525 if (msginfo && MSG_IS_COPY(msginfo->flags) && msginfo->to_folder) { 4526 summaryview->mlist = 4527 g_slist_prepend(summaryview->mlist, msginfo); 4528 4529 summary_msginfo_unset_flags(msginfo, 0, MSG_COPY); 4530 summary_set_row_marks(summaryview, node); 4531 } 4532 } 4533 4534 static void summary_execute_delete(SummaryView *summaryview) 4535 { 4536 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4537 GSList *cur; 4538 4539 /* search deleting messages and execute */ 4540 gtk_cmctree_pre_recursive 4541 (ctree, NULL, summary_execute_delete_func, summaryview); 4542 4543 if (!summaryview->mlist) return; 4544 4545 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, 4546 summaryview->msginfo_update_callback_id); 4547 4548 folder_item_remove_msgs(summaryview->folder_item, 4549 summaryview->mlist); 4550 4551 summaryview->msginfo_update_callback_id = 4552 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 4553 summary_update_msg, (gpointer) summaryview); 4554 4555 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) { 4556 procmsg_msginfo_free((MsgInfo **)&(cur->data)); 4557 } 4558 4559 g_slist_free(summaryview->mlist); 4560 summaryview->mlist = NULL; 4561 } 4562 4563 static void summary_execute_delete_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 4564 gpointer data) 4565 { 4566 SummaryView *summaryview = data; 4567 MsgInfo *msginfo; 4568 4569 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4570 4571 if (msginfo && MSG_IS_DELETED(msginfo->flags)) { 4572 summaryview->mlist = 4573 g_slist_prepend(summaryview->mlist, msginfo); 4574 gtk_cmctree_node_set_row_data(ctree, node, NULL); 4575 4576 if (msginfo->msgid && *msginfo->msgid && 4577 node == g_hash_table_lookup(summaryview->msgid_table, 4578 msginfo->msgid)) { 4579 g_hash_table_remove(summaryview->msgid_table, 4580 msginfo->msgid); 4581 } 4582 if (prefs_common.thread_by_subject && 4583 msginfo->subject && *msginfo->subject && 4584 node == subject_table_lookup(summaryview->subject_table, 4585 msginfo->subject)) { 4586 subject_table_remove(summaryview->subject_table, 4587 msginfo->subject); 4588 } 4589 } 4590 } 4591 4592 static void summary_execute_expunge_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 4593 gpointer data) 4594 { 4595 SummaryView *summaryview = data; 4596 MsgInfo *msginfo; 4597 4598 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4599 4600 if (msginfo && MSG_IS_DELETED(msginfo->flags)) { 4601 summaryview->mlist = 4602 g_slist_prepend(summaryview->mlist, msginfo); 4603 gtk_cmctree_node_set_row_data(ctree, node, NULL); 4604 4605 if (msginfo->msgid && *msginfo->msgid && 4606 node == g_hash_table_lookup(summaryview->msgid_table, 4607 msginfo->msgid)) { 4608 g_hash_table_remove(summaryview->msgid_table, 4609 msginfo->msgid); 4610 } 4611 if (prefs_common.thread_by_subject && 4612 msginfo->subject && *msginfo->subject && 4613 node == subject_table_lookup(summaryview->subject_table, 4614 msginfo->subject)) { 4615 subject_table_remove(summaryview->subject_table, 4616 msginfo->subject); 4617 } 4618 } 4619 } 4620 4621 static void summary_execute_expunge(SummaryView *summaryview) 4622 { 4623 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4624 GSList *cur; 4625 4626 gtk_cmctree_pre_recursive 4627 (ctree, NULL, summary_execute_expunge_func, summaryview); 4628 4629 hooks_unregister_hook(MSGINFO_UPDATE_HOOKLIST, 4630 summaryview->msginfo_update_callback_id); 4631 4632 folder_item_expunge(summaryview->folder_item); 4633 4634 summaryview->msginfo_update_callback_id = 4635 hooks_register_hook(MSGINFO_UPDATE_HOOKLIST, 4636 summary_update_msg, (gpointer) summaryview); 4637 for (cur = summaryview->mlist; cur != NULL && cur->data != NULL; cur = cur->next) 4638 procmsg_msginfo_free((MsgInfo **)&(cur->data)); 4639 4640 g_slist_free(summaryview->mlist); 4641 summaryview->mlist = NULL; 4642 } 4643 4644 /* thread functions */ 4645 4646 static void summary_thread_build(SummaryView *summaryview) 4647 { 4648 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4649 GtkCMCTreeNode *node; 4650 GtkCMCTreeNode *next; 4651 GtkCMCTreeNode *parent; 4652 MsgInfo *msginfo; 4653 GSList *reflist; 4654 4655 summary_lock(summaryview); 4656 4657 debug_print("Building threads...\n"); 4658 STATUSBAR_PUSH(summaryview->mainwin, "Building threads..."); 4659 main_window_cursor_wait(summaryview->mainwin); 4660 4661 g_signal_handlers_block_by_func(G_OBJECT(ctree), 4662 G_CALLBACK(summary_tree_expanded), summaryview); 4663 summary_freeze(summaryview); 4664 4665 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 4666 while (node) { 4667 next = GTK_CMCTREE_ROW(node)->sibling; 4668 4669 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4670 4671 parent = NULL; 4672 4673 if (msginfo && msginfo->inreplyto) { 4674 parent = g_hash_table_lookup(summaryview->msgid_table, 4675 msginfo->inreplyto); 4676 4677 if (!parent && msginfo->references) { 4678 for (reflist = msginfo->references; 4679 reflist != NULL; reflist = reflist->next) 4680 if ((parent = g_hash_table_lookup 4681 (summaryview->msgid_table, 4682 reflist->data))) 4683 break; 4684 } 4685 } 4686 4687 if (msginfo && prefs_common.thread_by_subject && parent == NULL) { 4688 parent = subject_table_lookup 4689 (summaryview->subject_table, 4690 msginfo->subject); 4691 } 4692 4693 if (parent && parent != node && parent != GTK_CMCTREE_ROW(node)->parent) { 4694 gtk_cmctree_move(ctree, node, parent, NULL); 4695 } 4696 4697 node = next; 4698 } 4699 4700 gtkut_ctree_set_focus_row(ctree, summaryview->selected); 4701 4702 summary_thaw(summaryview); 4703 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), 4704 G_CALLBACK(summary_tree_expanded), summaryview); 4705 4706 debug_print("Building threads done.\n"); 4707 STATUSBAR_POP(summaryview->mainwin); 4708 main_window_cursor_normal(summaryview->mainwin); 4709 4710 summaryview->threaded = TRUE; 4711 4712 summary_unlock(summaryview); 4713 } 4714 4715 static void summary_thread_init(SummaryView *summaryview) 4716 { 4717 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4718 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 4719 GtkCMCTreeNode *next; 4720 if (!summaryview->thread_collapsed) { 4721 g_signal_handlers_block_by_func(G_OBJECT(ctree), 4722 G_CALLBACK(summary_tree_expanded), summaryview); 4723 while (node) { 4724 next = GTK_CMCTREE_ROW(node)->sibling; 4725 if (GTK_CMCTREE_ROW(node)->children) 4726 gtk_cmctree_expand_recursive(ctree, node); 4727 node = next; 4728 } 4729 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), 4730 G_CALLBACK(summary_tree_expanded), summaryview); 4731 } 4732 } 4733 4734 static void summary_unthread_for_exec(SummaryView *summaryview) 4735 { 4736 GtkCMCTreeNode *node; 4737 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4738 gboolean froze = FALSE; 4739 4740 debug_print("Unthreading for execution...\n"); 4741 4742 START_LONG_OPERATION(summaryview, TRUE); 4743 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 4744 node != NULL; node = GTK_CMCTREE_NODE_NEXT(node)) { 4745 summary_unthread_for_exec_func(ctree, node, summaryview); 4746 } 4747 4748 END_LONG_OPERATION(summaryview); 4749 4750 debug_print("Unthreading for execution done.\n"); 4751 } 4752 4753 static void summary_unthread_for_exec_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 4754 gpointer data) 4755 { 4756 MsgInfo *msginfo; 4757 GtkCMCTreeNode *top_parent; 4758 GtkCMCTreeNode *child; 4759 GtkCMCTreeNode *sibling; 4760 SummaryView * summaryview = (SummaryView *)data; 4761 msginfo = GTKUT_CTREE_NODE_GET_ROW_DATA(node); 4762 4763 if (!msginfo || 4764 (!MSG_IS_MOVE(msginfo->flags) && 4765 !MSG_IS_DELETED(msginfo->flags))) 4766 return; 4767 child = GTK_CMCTREE_ROW(node)->children; 4768 if (!child) return; 4769 4770 if (node == summaryview->selected) 4771 summaryview->selected = NULL; 4772 if (node == summaryview->displayed) 4773 summaryview->displayed = NULL; 4774 4775 summary_cancel_mark_read_timeout(summaryview); 4776 4777 for (top_parent = node; 4778 GTK_CMCTREE_ROW(top_parent)->parent != NULL; 4779 top_parent = GTK_CMCTREE_ROW(top_parent)->parent) 4780 ; 4781 sibling = GTK_CMCTREE_ROW(top_parent)->sibling; 4782 4783 GTK_SCTREE(ctree)->sorting = TRUE; 4784 while (child != NULL) { 4785 GtkCMCTreeNode *next_child; 4786 MsgInfo *cinfo = GTKUT_CTREE_NODE_GET_ROW_DATA(child); 4787 4788 next_child = GTK_CMCTREE_ROW(child)->sibling; 4789 4790 if (!MSG_IS_MOVE(cinfo->flags) && !MSG_IS_DELETED(cinfo->flags)) { 4791 gtk_cmctree_move(ctree, child, 4792 NULL, 4793 sibling); 4794 } else { 4795 if (child == summaryview->displayed) { 4796 messageview_clear(summaryview->messageview); 4797 summaryview->displayed = NULL; 4798 } 4799 if (child == summaryview->selected) { 4800 messageview_clear(summaryview->messageview); 4801 summaryview->selected = NULL; 4802 } 4803 } 4804 child = next_child; 4805 } 4806 GTK_SCTREE(ctree)->sorting = FALSE; 4807 } 4808 4809 void summary_expand_threads(SummaryView *summaryview) 4810 { 4811 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4812 GtkCMCTreeNode *node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 4813 GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row)); 4814 4815 g_signal_handlers_block_by_func(G_OBJECT(ctree), 4816 G_CALLBACK(summary_tree_expanded), summaryview); 4817 summary_freeze(summaryview); 4818 GTK_SCTREE(ctree)->sorting = TRUE; 4819 4820 while (node) { 4821 if (GTK_CMCTREE_ROW(node)->children) { 4822 gtk_cmctree_expand(ctree, node); 4823 summary_set_row_marks(summaryview, node); 4824 } 4825 node = GTK_CMCTREE_NODE_NEXT(node); 4826 } 4827 4828 GTK_SCTREE(ctree)->sorting = FALSE; 4829 if (focus_node) { 4830 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node); 4831 } 4832 summary_thaw(summaryview); 4833 4834 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), 4835 G_CALLBACK(summary_tree_expanded), summaryview); 4836 4837 summaryview->thread_collapsed = FALSE; 4838 4839 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 4840 } 4841 4842 void summary_collapse_threads(SummaryView *summaryview) 4843 { 4844 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 4845 GtkCMCTreeNode *node = NULL; 4846 GtkCMCTreeNode *focus_node = GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST(ctree)->row_list, GTK_CMCLIST(ctree)->focus_row)); 4847 4848 g_signal_handlers_block_by_func(G_OBJECT(ctree), 4849 G_CALLBACK(summary_tree_collapsed), summaryview); 4850 summary_freeze(summaryview); 4851 GTK_SCTREE(ctree)->sorting = TRUE; 4852 4853 node = focus_node; 4854 while (node && GTK_CMCTREE_ROW(node)->parent) { 4855 focus_node = node = GTK_CMCTREE_ROW(node)->parent; 4856 } 4857 summary_select_node(summaryview, node, OPEN_SELECTED_ON_DIRECTIONAL); 4858 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 4859 while (node) { 4860 if (GTK_CMCTREE_ROW(node)->children) { 4861 gtk_cmctree_collapse(ctree, node); 4862 summary_set_row_marks(summaryview, node); 4863 } 4864 node = GTK_CMCTREE_ROW(node)->sibling; 4865 } 4866 4867 GTK_SCTREE(ctree)->sorting = FALSE; 4868 if (focus_node) { 4869 GTK_CMCLIST(ctree)->focus_row = g_list_position (GTK_CMCLIST(ctree)->row_list,(GList *)focus_node); 4870 } 4871 GTK_SCTREE(ctree)->anchor_row = 4872 gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row); 4873 summary_thaw(summaryview); 4874 g_signal_handlers_unblock_by_func(G_OBJECT(ctree), 4875 G_CALLBACK(summary_tree_collapsed), summaryview); 4876 4877 summaryview->thread_collapsed = TRUE; 4878 4879 gtk_cmctree_node_moveto(ctree, summaryview->selected, 0, 0.5, 0); 4880 } 4881 4882 void summaryview_destroy(SummaryView *summaryview) 4883 { 4884 if(summaryview->simplify_subject_preg) { 4885 regfree(summaryview->simplify_subject_preg); 4886 g_free(summaryview->simplify_subject_preg); 4887 summaryview->simplify_subject_preg = NULL; 4888 } 4889 } 4890 4891 static gboolean summary_popup_menu(GtkWidget *widget, gpointer data) 4892 { 4893 SummaryView *summaryview = (SummaryView *)data; 4894 summaryview->display_msg = messageview_is_visible(summaryview->messageview); 4895 4896 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL); 4897 4898 return TRUE; 4899 } 4900 4901 static gchar *summaryview_get_tooltip_text(SummaryView *summaryview, MsgInfo *info, gint column) 4902 { 4903 MsgFlags flags; 4904 if (!info) 4905 return NULL; 4906 4907 flags = info->flags; 4908 4909 switch(summaryview->col_state[column].type) { 4910 case S_COL_STATUS: 4911 if (MSG_IS_IGNORE_THREAD(flags)) { 4912 return "Ignored thread"; 4913 } else if (MSG_IS_WATCH_THREAD(flags)) { 4914 return "Watched thread"; 4915 } else if (MSG_IS_SPAM(flags)) { 4916 return "Spam"; 4917 } else if (MSG_IS_NEW(flags)) { 4918 return "New"; 4919 } else if (MSG_IS_UNREAD(flags)) { 4920 return "Unread"; 4921 } else if (MSG_IS_REPLIED(flags) && MSG_IS_FORWARDED(flags)) { 4922 return "Replied but also forwarded - click to see reply"; 4923 } else if (MSG_IS_REPLIED(flags)) { 4924 return "Replied - click to see reply"; 4925 } else if (MSG_IS_FORWARDED(flags)) { 4926 return "Forwarded"; 4927 } else { 4928 return NULL; 4929 } 4930 case S_COL_MARK: 4931 if (MSG_IS_DELETED(flags)) { 4932 return "Deleted"; 4933 } else if (MSG_IS_MARKED(flags)) { 4934 return "Marked"; 4935 } else if (MSG_IS_MOVE(flags)) { 4936 return "To be moved"; 4937 } else if (MSG_IS_COPY(flags)) { 4938 return "To be copied"; 4939 } else { 4940 return NULL; 4941 } 4942 case S_COL_LOCKED: 4943 if (MSG_IS_LOCKED(flags)) { 4944 return "Locked"; 4945 } else { 4946 return NULL; 4947 } 4948 case S_COL_MIME: 4949 if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_SIGNED(flags)) { 4950 return "Signed, has attachment(s)"; 4951 } else if (MSG_IS_SIGNED(flags)) { 4952 return "Signed"; 4953 } else if (MSG_IS_WITH_ATTACHMENT(flags) && MSG_IS_ENCRYPTED(flags)) { 4954 return "Encrypted, has attachment(s)"; 4955 } else if (MSG_IS_ENCRYPTED(flags)) { 4956 return "Encrypted"; 4957 } else if (MSG_IS_WITH_ATTACHMENT(flags)) { 4958 return "Has attachment(s)"; 4959 } else { 4960 return NULL; 4961 } 4962 default: 4963 return NULL; 4964 } 4965 } 4966 4967 static gboolean tooltip_cb (GtkWidget *widget, 4968 gint x, 4969 gint y, 4970 gboolean keyboard_mode, 4971 GtkTooltip *tooltip, 4972 gpointer user_data) 4973 { 4974 GtkCMCTree *ctree = GTK_CMCTREE(widget); 4975 SummaryView *summaryview = (SummaryView *)user_data; 4976 gint row = -1, column = -1; 4977 int offset = prefs_common.show_col_headers ? 24:0; 4978 GtkCMCTreeNode *node = NULL; 4979 gchar *text = NULL; 4980 gchar *formatted = NULL; 4981 MsgInfo *info = NULL; 4982 GdkRectangle rect; 4983 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT); 4984 if (!prefs_common.show_tooltips) 4985 return FALSE; 4986 4987 if (y - offset < 0) 4988 return FALSE; 4989 4990 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), x, y - offset, 4991 &row, &column)) 4992 return FALSE; 4993 4994 if ((node = gtk_cmctree_node_nth(ctree, row)) == NULL) 4995 return FALSE; 4996 4997 if ((info = gtk_cmctree_node_get_row_data(ctree, node)) == NULL) 4998 return FALSE; 4999 5000 switch (gtk_cmctree_node_get_cell_type(ctree, node, column)) { 5001 case GTK_CMCELL_TEXT: 5002 if (gtk_cmctree_node_get_text(ctree, node, column, &text) != TRUE) 5003 return FALSE; 5004 if (column == summaryview->col_pos[S_COL_FROM]) 5005 text = info->from; 5006 else if (column == summaryview->col_pos[S_COL_TO]) 5007 text = info->to; 5008 break; 5009 case GTK_CMCELL_PIXTEXT: 5010 if (gtk_cmctree_node_get_pixtext(ctree, node, column, &text, 5011 NULL, NULL) != TRUE) 5012 return FALSE; 5013 break; 5014 default: 5015 if ((text = summaryview_get_tooltip_text(summaryview, info, column)) == NULL) 5016 return FALSE; 5017 } 5018 5019 if (!text || !*text) 5020 return FALSE; 5021 5022 formatted = g_strdup(text); 5023 g_strstrip(formatted); 5024 5025 if (vert_layout && prefs_common.two_line_vert) 5026 gtk_tooltip_set_markup (tooltip, formatted); 5027 else 5028 gtk_tooltip_set_text (tooltip, formatted); 5029 g_free(formatted); 5030 5031 rect.x = x - 2; 5032 rect.y = y - 2; 5033 rect.width = 12; 5034 rect.height= 12; 5035 gtk_tooltip_set_tip_area(tooltip, &rect); 5036 5037 return TRUE; 5038 } 5039 5040 static gboolean summary_header_button_pressed(GtkWidget *widget, 5041 GdkEvent *_event, 5042 gpointer user_data) 5043 { 5044 GdkEventButton *event = (GdkEventButton *)_event; 5045 SummaryView *summaryview = (SummaryView *)user_data; 5046 5047 cm_return_val_if_fail(summaryview != NULL, FALSE); 5048 5049 /* Only handle single button presses. */ 5050 if (event->type == GDK_2BUTTON_PRESS || 5051 event->type == GDK_3BUTTON_PRESS) 5052 return FALSE; 5053 5054 /* Handle right-click for context menu */ 5055 if (event->button == 3) { 5056 /* Set up any menu items that need setting up. */ 5057 summaryview->header_menu_lock = TRUE; 5058 cm_toggle_menu_set_active_full(summaryview->mainwin->ui_manager, 5059 "Menus/SummaryViewHeaderPopup/LockColumnHeaders", 5060 prefs_common_get_prefs()->summary_col_lock); 5061 summaryview->header_menu_lock = FALSE; 5062 5063 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->headerpopupmenu), NULL); 5064 return TRUE; 5065 } 5066 5067 return FALSE; 5068 } 5069 5070 static GtkWidget *summary_ctree_create(SummaryView *summaryview) 5071 { 5072 GtkWidget *ctree; 5073 gint *col_pos = summaryview->col_pos; 5074 SummaryColumnState *col_state; 5075 gchar *titles[N_SUMMARY_COLS]; 5076 SummaryColumnType type; 5077 gint pos; 5078 gboolean vert_layout = (prefs_common.layout_mode == VERTICAL_LAYOUT); 5079 memset(titles, 0, sizeof(titles)); 5080 5081 col_state = prefs_summary_column_get_config(); 5082 for (pos = 0; pos < N_SUMMARY_COLS; pos++) { 5083 summaryview->col_state[pos] = col_state[pos]; 5084 type = col_state[pos].type; 5085 col_pos[type] = pos; 5086 titles[pos] = "dummy"; 5087 } 5088 col_state = summaryview->col_state; 5089 5090 ctree = gtk_sctree_new_with_titles 5091 (N_SUMMARY_COLS, col_pos[S_COL_SUBJECT], titles); 5092 5093 gtk_widget_set_name(GTK_WIDGET(ctree), "summaryview_sctree"); 5094 5095 if (prefs_common.show_col_headers == FALSE) 5096 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree)); 5097 5098 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_MULTIPLE); 5099 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MARK], 5100 GTK_JUSTIFY_CENTER); 5101 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS], 5102 GTK_JUSTIFY_CENTER); 5103 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED], 5104 GTK_JUSTIFY_CENTER); 5105 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_MIME], 5106 GTK_JUSTIFY_CENTER); 5107 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE], 5108 GTK_JUSTIFY_RIGHT); 5109 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER], 5110 GTK_JUSTIFY_RIGHT); 5111 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE], 5112 GTK_JUSTIFY_RIGHT); 5113 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MARK], 5114 prefs_common.summary_col_size[S_COL_MARK]); 5115 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_STATUS], 5116 prefs_common.summary_col_size[S_COL_STATUS]); 5117 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_LOCKED], 5118 prefs_common.summary_col_size[S_COL_LOCKED]); 5119 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_MIME], 5120 prefs_common.summary_col_size[S_COL_MIME]); 5121 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SUBJECT], 5122 prefs_common.summary_col_size[S_COL_SUBJECT]); 5123 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_FROM], 5124 prefs_common.summary_col_size[S_COL_FROM]); 5125 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TO], 5126 prefs_common.summary_col_size[S_COL_TO]); 5127 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_DATE], 5128 prefs_common.summary_col_size[S_COL_DATE]); 5129 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SIZE], 5130 prefs_common.summary_col_size[S_COL_SIZE]); 5131 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_NUMBER], 5132 prefs_common.summary_col_size[S_COL_NUMBER]); 5133 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_SCORE], 5134 prefs_common.summary_col_size[S_COL_SCORE]); 5135 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[S_COL_TAGS], 5136 prefs_common.summary_col_size[S_COL_TAGS]); 5137 5138 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree), 5139 GTK_CMCTREE_EXPANDER_TRIANGLE); 5140 5141 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries); 5142 5143 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), 12); 5144 g_object_set_data(G_OBJECT(ctree), "summaryview", (gpointer)summaryview); 5145 5146 for (pos = 0; pos < N_SUMMARY_COLS; pos++) { 5147 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[pos].button, 5148 FALSE); 5149 if (((pos == summaryview->col_pos[S_COL_FROM] && !FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) || 5150 (pos == summaryview->col_pos[S_COL_TO] && FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) || 5151 pos == summaryview->col_pos[S_COL_DATE]) && vert_layout && 5152 prefs_common.two_line_vert) 5153 gtk_cmclist_set_column_visibility 5154 (GTK_CMCLIST(ctree), pos, FALSE); 5155 else 5156 gtk_cmclist_set_column_visibility 5157 (GTK_CMCLIST(ctree), pos, col_state[pos].visible); 5158 } 5159 if (prefs_common.two_line_vert) 5160 gtk_sctree_set_use_markup(GTK_SCTREE(ctree), summaryview->col_pos[S_COL_SUBJECT], vert_layout); 5161 5162 /* connect signal to the buttons for sorting */ 5163 #define CLIST_BUTTON_SIGNAL_CONNECT(col, func) \ 5164 g_signal_connect \ 5165 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \ 5166 "button-press-event", \ 5167 G_CALLBACK(summary_header_button_pressed), \ 5168 summaryview); \ 5169 g_signal_connect \ 5170 (G_OBJECT(GTK_CMCLIST(ctree)->column[col_pos[col]].button), \ 5171 "clicked", \ 5172 G_CALLBACK(func), \ 5173 summaryview); 5174 5175 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MARK , summary_mark_clicked) 5176 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_STATUS , summary_status_clicked) 5177 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_MIME , summary_mime_clicked) 5178 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_NUMBER , summary_num_clicked) 5179 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SIZE , summary_size_clicked) 5180 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_DATE , summary_date_clicked) 5181 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_FROM , summary_from_clicked) 5182 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_TO , summary_to_clicked) 5183 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SUBJECT, summary_subject_clicked) 5184 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_SCORE , summary_score_clicked) 5185 CLIST_BUTTON_SIGNAL_CONNECT(S_COL_LOCKED , summary_locked_clicked) 5186 5187 #undef CLIST_BUTTON_SIGNAL_CONNECT 5188 5189 g_signal_connect(G_OBJECT(ctree), "tree_select_row", 5190 G_CALLBACK(summary_selected), summaryview); 5191 g_signal_connect(G_OBJECT(ctree), "tree_unselect_row", 5192 G_CALLBACK(summary_unselected), summaryview); 5193 g_signal_connect(G_OBJECT(ctree), "button_press_event", 5194 G_CALLBACK(summary_button_pressed), 5195 summaryview); 5196 g_signal_connect(G_OBJECT(ctree), "popup-menu", 5197 G_CALLBACK(summary_popup_menu), summaryview); 5198 g_signal_connect(G_OBJECT(ctree), "button_release_event", 5199 G_CALLBACK(summary_button_released), 5200 summaryview); 5201 g_signal_connect(G_OBJECT(ctree), "key_press_event", 5202 G_CALLBACK(summary_key_pressed), summaryview); 5203 g_signal_connect(G_OBJECT(ctree), "resize_column", 5204 G_CALLBACK(summary_col_resized), summaryview); 5205 g_signal_connect(G_OBJECT(ctree), "open_row", 5206 G_CALLBACK(summary_open_row), summaryview); 5207 5208 g_signal_connect_after(G_OBJECT(ctree), "tree_expand", 5209 G_CALLBACK(summary_tree_expanded), 5210 summaryview); 5211 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse", 5212 G_CALLBACK(summary_tree_collapsed), 5213 summaryview); 5214 5215 g_object_set (G_OBJECT(ctree), "has-tooltip", TRUE, NULL); 5216 g_signal_connect(G_OBJECT(ctree), "query-tooltip", 5217 G_CALLBACK(tooltip_cb), 5218 summaryview); 5219 return ctree; 5220 } 5221 5222 void summary_set_column_order(SummaryView *summaryview) 5223 { 5224 GtkWidget *ctree; 5225 GtkWidget *scrolledwin = summaryview->scrolledwin; 5226 FolderItem *item; 5227 guint selected_msgnum = summary_get_msgnum(summaryview, summaryview->selected); 5228 guint displayed_msgnum = summary_get_msgnum(summaryview, summaryview->displayed); 5229 5230 item = summaryview->folder_item; 5231 5232 summary_clear_all(summaryview); 5233 gtk_widget_destroy(summaryview->ctree); 5234 5235 summaryview->ctree = ctree = summary_ctree_create(summaryview); 5236 summary_set_column_titles(summaryview); 5237 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 5238 GTK_CMCLIST(ctree)->hadjustment); 5239 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 5240 GTK_CMCLIST(ctree)->vadjustment); 5241 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree); 5242 gtk_widget_show(ctree); 5243 5244 summary_show(summaryview, item, FALSE); 5245 5246 summary_select_by_msgnum(summaryview, selected_msgnum, FALSE); 5247 5248 summaryview->selected = summary_find_msg_by_msgnum(summaryview, selected_msgnum); 5249 summaryview->displayed = summary_find_msg_by_msgnum(summaryview, displayed_msgnum); 5250 if (!summaryview->displayed) 5251 messageview_clear(summaryview->messageview); 5252 else 5253 summary_redisplay_msg(summaryview); 5254 5255 /* get normal row height */ 5256 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0); 5257 normal_row_height = GTK_CMCLIST(ctree)->row_height; 5258 5259 if ((prefs_common.layout_mode == VERTICAL_LAYOUT) && 5260 prefs_common.two_line_vert) { 5261 gtk_cmclist_set_row_height(GTK_CMCLIST(summaryview->ctree), 2*normal_row_height + 2); 5262 } 5263 } 5264 5265 5266 /* callback functions */ 5267 5268 static gint summary_folder_eventbox_pressed(GtkWidget *eventbox, GdkEventButton *event, 5269 SummaryView *summaryview) 5270 { 5271 if (event) { 5272 folderview_grab_focus(summaryview->folderview); 5273 mainwindow_exit_folder(summaryview->mainwin); 5274 } 5275 return TRUE; 5276 } 5277 5278 static gint summary_toggle_pressed(GtkWidget *eventbox, GdkEventButton *event, 5279 SummaryView *summaryview) 5280 { 5281 if (event) 5282 summary_toggle_view(summaryview); 5283 return TRUE; 5284 } 5285 5286 static gboolean summary_button_pressed(GtkWidget *ctree, GdkEventButton *event, 5287 SummaryView *summaryview) 5288 { 5289 if (!event) return FALSE; 5290 if (event->window != GTK_CMCLIST(ctree)->clist_window) return FALSE; 5291 5292 if (event->button == 3) { 5293 /* right clicked */ 5294 summary_set_menu_sensitive(summaryview); 5295 gtk_menu_popup_at_pointer(GTK_MENU(summaryview->popupmenu), NULL); 5296 } else if (event->button == 2) { 5297 summaryview->display_msg = messageview_is_visible(summaryview->messageview); 5298 } else if (event->button == 1) { 5299 if (!prefs_common.emulate_emacs && 5300 messageview_is_visible(summaryview->messageview)) 5301 summaryview->display_msg = TRUE; 5302 } 5303 5304 return FALSE; 5305 } 5306 5307 static gboolean summary_button_released(GtkWidget *ctree, GdkEventButton *event, 5308 SummaryView *summaryview) 5309 { 5310 return FALSE; 5311 } 5312 5313 gboolean summary_pass_key_press_event(SummaryView *summaryview, GdkEventKey *event) 5314 { 5315 if (!summaryview) 5316 return FALSE; 5317 if (summary_is_list(summaryview)) 5318 return summary_key_pressed(summaryview->ctree, event, summaryview); 5319 else 5320 return FALSE; 5321 } 5322 5323 #define BREAK_ON_MODIFIER_KEY() \ 5324 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break 5325 5326 /* Copied from gtkcmclist.c, if it changes there, it has to change 5327 * here as well. This is an ugly hack, there must be a better way to 5328 * find out how much to move for page up/down. */ 5329 #define CELL_SPACING 1 5330 static gboolean summary_key_pressed(GtkWidget *widget, GdkEventKey *event, 5331 SummaryView *summaryview) 5332 { 5333 GtkCMCTree *ctree = GTK_CMCTREE(widget); 5334 GtkCMCTreeNode *node; 5335 GtkAdjustment *adj; 5336 gboolean mod_pressed; 5337 5338 if (!event) 5339 return TRUE; 5340 5341 mod_pressed = 5342 ((event->state & (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0); 5343 5344 if (summaryview->selected) { 5345 gboolean handled = FALSE; 5346 switch (event->keyval) { 5347 case GDK_KEY_space: 5348 handled = TRUE; 5349 if (event->state & GDK_CONTROL_MASK) { 5350 handled = FALSE; 5351 break; 5352 } 5353 if (summaryview->displayed != summaryview->selected) { 5354 summary_display_msg(summaryview, summaryview->selected); 5355 } else { 5356 if (mod_pressed) { 5357 summary_select_prev_unread(summaryview); 5358 } else { 5359 summary_select_next_unread(summaryview); 5360 } 5361 } 5362 break; 5363 case GDK_KEY_Return: 5364 case GDK_KEY_KP_Enter: 5365 handled = TRUE; 5366 if (summaryview->displayed != summaryview->selected) 5367 summary_display_msg(summaryview, summaryview->selected); 5368 break; 5369 } 5370 5371 if (handled) 5372 return FALSE; 5373 } 5374 if (summary_is_locked(summaryview)) 5375 return TRUE; 5376 5377 switch (event->keyval) { 5378 case GDK_KEY_Left: /* Move focus */ 5379 adj = gtk_scrolled_window_get_hadjustment 5380 (GTK_SCROLLED_WINDOW(summaryview->scrolledwin)); 5381 if (gtk_adjustment_get_lower(adj) != gtk_adjustment_get_value(adj)) 5382 break; 5383 folderview_grab_focus(summaryview->folderview); 5384 mainwindow_exit_folder(summaryview->mainwin); 5385 return TRUE; 5386 case GDK_KEY_Home: case GDK_KEY_KP_Home: 5387 case GDK_KEY_End: case GDK_KEY_KP_End: 5388 case GDK_KEY_Up: case GDK_KEY_KP_Up: 5389 case GDK_KEY_Down: case GDK_KEY_KP_Down: 5390 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up: 5391 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down: 5392 if ((node = summaryview->selected) != NULL) { 5393 GtkCMCTreeNode *next = NULL; 5394 switch (event->keyval) { 5395 case GDK_KEY_Home: case GDK_KEY_KP_Home: 5396 next = gtk_cmctree_node_nth(ctree, 0); 5397 break; 5398 case GDK_KEY_End: case GDK_KEY_KP_End: 5399 next = gtk_cmctree_node_nth(ctree, 5400 g_list_length(GTK_CMCLIST(ctree)->row_list)-1); 5401 break; 5402 case GDK_KEY_Up: case GDK_KEY_KP_Up: 5403 next = gtk_cmctree_node_nth(ctree, 5404 MAX(0, GTK_CMCLIST(ctree)->focus_row - 1)); 5405 break; 5406 case GDK_KEY_Down: case GDK_KEY_KP_Down: 5407 next = gtk_cmctree_node_nth(ctree, 5408 MIN(GTK_CMCLIST(ctree)->focus_row + 1, GTK_CMCLIST(ctree)->rows - 1)); 5409 break; 5410 case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up: 5411 next = gtk_cmctree_node_nth(ctree, 5412 MAX(0, GTK_CMCLIST(ctree)->focus_row - 5413 (2 * GTK_CMCLIST(ctree)->clist_window_height - 5414 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) / 5415 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING)))); 5416 break; 5417 case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down: 5418 next = gtk_cmctree_node_nth(ctree, 5419 MIN(GTK_CMCLIST(ctree)->rows - 1, GTK_CMCLIST(ctree)->focus_row + 5420 (2 * GTK_CMCLIST(ctree)->clist_window_height - 5421 GTK_CMCLIST(ctree)->row_height - CELL_SPACING) / 5422 (2 * (GTK_CMCLIST(ctree)->row_height + CELL_SPACING)))); 5423 break; 5424 } 5425 5426 if (next != NULL && 5427 next != gtk_cmctree_node_nth(ctree, GTK_CMCLIST(ctree)->focus_row)) { 5428 gtk_sctree_select_with_state 5429 (GTK_SCTREE(ctree), next, (event->state & ~GDK_CONTROL_MASK) ); 5430 5431 /* Deprecated - what are the non-deprecated equivalents? */ 5432 if (gtk_cmctree_node_is_visible(GTK_CMCTREE(ctree), next) != GTK_VISIBILITY_FULL) 5433 gtkut_ctree_node_move_if_on_the_edge(GTK_CMCTREE(ctree), next, -1); 5434 if (!mod_pressed) 5435 summary_select_node(summaryview, summaryview->selected, OPEN_SELECTED_ON_DIRECTIONAL); 5436 summaryview->selected = next; 5437 } 5438 } 5439 return TRUE; 5440 default: 5441 break; 5442 } 5443 5444 if (!summaryview->selected) { 5445 node = gtk_cmctree_node_nth(ctree, 0); 5446 if (node) 5447 gtk_sctree_select(GTK_SCTREE(ctree), node); 5448 else 5449 return TRUE; 5450 } 5451 5452 switch (event->keyval) { 5453 case GDK_KEY_Delete: 5454 BREAK_ON_MODIFIER_KEY(); 5455 summary_delete_trash(summaryview); 5456 break; 5457 default: 5458 break; 5459 } 5460 return FALSE; 5461 } 5462 #undef CELL_SPACING 5463 5464 void summary_open_row(GtkSCTree *sctree, SummaryView *summaryview) 5465 { 5466 if (FOLDER_SHOWS_TO_HDR(summaryview->folder_item)) 5467 summary_reedit(summaryview); 5468 else 5469 summary_open_msg(summaryview, TRUE, FALSE); 5470 5471 summaryview->display_msg = FALSE; 5472 } 5473 5474 static void summary_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node, 5475 SummaryView *summaryview) 5476 { 5477 summary_set_row_marks(summaryview, node); 5478 if (prefs_common.bold_unread) { 5479 while (node) { 5480 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node); 5481 if (GTK_CMCTREE_ROW(node)->children) 5482 summary_set_row_marks(summaryview, node); 5483 node = next; 5484 } 5485 } 5486 } 5487 5488 static void summary_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node, 5489 SummaryView *summaryview) 5490 { 5491 gtk_sctree_select(GTK_SCTREE(ctree), node); 5492 summary_set_row_marks(summaryview, node); 5493 } 5494 5495 static void summary_unselected(GtkCMCTree *ctree, GtkCMCTreeNode *row, 5496 gint column, SummaryView *summaryview) 5497 { 5498 if (summary_is_locked(summaryview) 5499 || GTK_SCTREE(ctree)->selecting_range) { 5500 return; 5501 } 5502 5503 summary_status_show(summaryview); 5504 } 5505 5506 static void summary_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row, 5507 gint column, SummaryView *summaryview) 5508 { 5509 const GList *list, *cur; 5510 MessageView *msgview; 5511 MsgInfo *msginfo; 5512 gboolean marked_unread = FALSE; 5513 5514 if (summary_is_locked(summaryview) 5515 && !GTK_SCTREE(ctree)->selecting_range 5516 && summaryview->messageview 5517 && summaryview->messageview->mimeview 5518 && summaryview->messageview->mimeview->type == MIMEVIEW_TEXT 5519 && summaryview->messageview->mimeview->textview->loading) { 5520 PostponedSelectData *data = g_new0(PostponedSelectData, 1); 5521 summaryview->messageview->mimeview->textview->stop_loading = TRUE; 5522 5523 data->ctree = ctree; 5524 data->row = row; 5525 data->node = NULL; 5526 data->column = column; 5527 data->summaryview = summaryview; 5528 debug_print("postponing open of message till end of load\n"); 5529 g_timeout_add(100, summary_select_retry, data); 5530 return; 5531 } 5532 if (summary_is_locked(summaryview) 5533 || GTK_SCTREE(ctree)->selecting_range) { 5534 return; 5535 } 5536 5537 summary_status_show(summaryview); 5538 5539 if (GTK_CMCLIST(ctree)->selection && 5540 GTK_CMCLIST(ctree)->selection->next) { 5541 summaryview->display_msg = FALSE; 5542 summary_set_menu_sensitive(summaryview); 5543 toolbar_main_set_sensitive(summaryview->mainwin); 5544 return; 5545 } 5546 5547 summaryview->selected = row; 5548 5549 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 5550 cm_return_if_fail(msginfo != NULL); 5551 5552 main_create_mailing_list_menu (summaryview->mainwin, msginfo); 5553 5554 switch (column < 0 ? column : summaryview->col_state[column].type) { 5555 case S_COL_MARK: 5556 if (!MSG_IS_DELETED(msginfo->flags) && 5557 !MSG_IS_MOVE(msginfo->flags) && 5558 !MSG_IS_COPY(msginfo->flags)) { 5559 if (MSG_IS_MARKED(msginfo->flags)) { 5560 summary_unmark_row(summaryview, row); 5561 summary_status_show(summaryview); 5562 } else { 5563 summary_mark_row(summaryview, row); 5564 summary_status_show(summaryview); 5565 } 5566 } 5567 break; 5568 case S_COL_STATUS: 5569 if (MSG_IS_UNREAD(msginfo->flags)) { 5570 summary_mark_row_as_read(summaryview, row); 5571 summary_status_show(summaryview); 5572 } else if (!MSG_IS_REPLIED(msginfo->flags) && 5573 !MSG_IS_FORWARDED(msginfo->flags)) { 5574 marked_unread = TRUE; 5575 } 5576 break; 5577 case S_COL_LOCKED: 5578 if (MSG_IS_LOCKED(msginfo->flags)) { 5579 summary_unlock_row(summaryview, row); 5580 summary_status_show(summaryview); 5581 } 5582 else { 5583 summary_lock_row(summaryview, row); 5584 summary_status_show(summaryview); 5585 } 5586 break; 5587 default: 5588 break; 5589 } 5590 5591 list = messageview_get_msgview_list(); 5592 for (cur = list; cur != NULL; cur = cur->next) { 5593 msgview = (MessageView *) cur->data; 5594 5595 if (msgview->new_window && msgview->update_needed) { 5596 MsgInfo *new_msginfo = summary_get_selected_msg(summaryview); 5597 messageview_show(msgview, new_msginfo, msgview->all_headers); 5598 msgview->update_needed = FALSE; 5599 } 5600 } 5601 5602 if (summaryview->display_msg) { 5603 summaryview->display_msg = FALSE; 5604 if (summaryview->displayed != row) { 5605 summary_display_msg(summaryview, row); 5606 if (marked_unread) { 5607 summary_mark_row_as_unread(summaryview, row); 5608 summary_status_show(summaryview); 5609 } 5610 return; 5611 } 5612 } 5613 5614 if (marked_unread) { 5615 summary_mark_row_as_unread(summaryview, row); 5616 summary_status_show(summaryview); 5617 } 5618 5619 summary_set_menu_sensitive(summaryview); 5620 toolbar_main_set_sensitive(summaryview->mainwin); 5621 } 5622 5623 static void summary_col_resized(GtkCMCList *clist, gint column, gint width, 5624 SummaryView *summaryview) 5625 { 5626 SummaryColumnType type = summaryview->col_state[column].type; 5627 5628 prefs_common.summary_col_size[type] = width; 5629 } 5630 5631 5632 /* 5633 * \brief get List of msginfo selected in SummaryView 5634 * 5635 * \param summaryview 5636 * 5637 * \return GSList holding MsgInfo 5638 */ 5639 GSList *summary_get_selection(SummaryView *summaryview) 5640 { 5641 GList *sel = NULL; 5642 GSList *msginfo_list = NULL; 5643 5644 cm_return_val_if_fail(summaryview != NULL, NULL); 5645 5646 sel = GTK_CMCLIST(summaryview->ctree)->selection; 5647 5648 cm_return_val_if_fail(sel != NULL, NULL); 5649 5650 for ( ; sel != NULL; sel = sel->next) 5651 msginfo_list = 5652 g_slist_prepend(msginfo_list, 5653 gtk_cmctree_node_get_row_data(GTK_CMCTREE(summaryview->ctree), 5654 GTK_CMCTREE_NODE(sel->data))); 5655 return g_slist_reverse(msginfo_list); 5656 } 5657 5658 /* 5659 * \brief get number of messages currently selected in SummaryView 5660 * 5661 * \param summaryview 5662 * 5663 * \return number of messages currently selected 5664 */ 5665 guint summary_get_selection_count(SummaryView *summaryview) 5666 { 5667 cm_return_val_if_fail(summaryview != NULL, 0); 5668 return g_list_length(GTK_CMCLIST(summaryview->ctree)->selection); 5669 } 5670 5671 static void summary_sort_by_column_click(SummaryView *summaryview, 5672 FolderSortKey sort_key) 5673 { 5674 if (prefs_common.summary_col_lock) { 5675 debug_print("summaryview columns locked, ignoring\n"); 5676 return; 5677 } 5678 5679 GtkCMCTreeNode *node = NULL; 5680 if (summaryview->sort_key == sort_key) 5681 summary_sort(summaryview, sort_key, 5682 summaryview->sort_type == SORT_ASCENDING 5683 ? SORT_DESCENDING : SORT_ASCENDING); 5684 else 5685 summary_sort(summaryview, sort_key, summaryview->sort_type); 5686 5687 node = GTK_CMCTREE_NODE(GTK_CMCLIST(summaryview->ctree)->row_list); 5688 5689 summary_freeze(summaryview); 5690 if (prefs_common.bold_unread || prefs_common.bold_marked) { 5691 while (node) { 5692 GtkCMCTreeNode *next = GTK_CMCTREE_NODE_NEXT(node); 5693 if (GTK_CMCTREE_ROW(node)->children) 5694 summary_set_row_marks(summaryview, node); 5695 node = next; 5696 } 5697 } 5698 summary_thaw(summaryview); 5699 } 5700 5701 static void summary_mark_clicked(GtkWidget *button, SummaryView *summaryview) 5702 { 5703 summary_sort_by_column_click(summaryview, SORT_BY_MARK); 5704 } 5705 5706 static void summary_status_clicked(GtkWidget *button, SummaryView *summaryview) 5707 { 5708 summary_sort_by_column_click(summaryview, SORT_BY_STATUS); 5709 } 5710 5711 static void summary_mime_clicked(GtkWidget *button, SummaryView *summaryview) 5712 { 5713 summary_sort_by_column_click(summaryview, SORT_BY_MIME); 5714 } 5715 5716 static void summary_num_clicked(GtkWidget *button, SummaryView *summaryview) 5717 { 5718 summary_sort_by_column_click(summaryview, SORT_BY_NUMBER); 5719 } 5720 5721 static void summary_size_clicked(GtkWidget *button, SummaryView *summaryview) 5722 { 5723 summary_sort_by_column_click(summaryview, SORT_BY_SIZE); 5724 } 5725 5726 static void summary_date_clicked(GtkWidget *button, SummaryView *summaryview) 5727 { 5728 if (summaryview->sort_key == SORT_BY_THREAD_DATE) 5729 summary_sort_by_column_click(summaryview, SORT_BY_THREAD_DATE); 5730 else 5731 summary_sort_by_column_click(summaryview, SORT_BY_DATE); 5732 } 5733 5734 static void summary_from_clicked(GtkWidget *button, SummaryView *summaryview) 5735 { 5736 if (summaryview->col_state[summaryview->col_pos[S_COL_FROM]].visible) 5737 summary_sort_by_column_click(summaryview, SORT_BY_FROM); 5738 else 5739 summary_sort_by_column_click(summaryview, SORT_BY_TO); 5740 } 5741 5742 static void summary_to_clicked(GtkWidget *button, SummaryView *summaryview) 5743 { 5744 if (summaryview->col_state[summaryview->col_pos[S_COL_TO]].visible) 5745 summary_sort_by_column_click(summaryview, SORT_BY_TO); 5746 else 5747 summary_sort_by_column_click(summaryview, SORT_BY_FROM); 5748 } 5749 5750 static void summary_subject_clicked(GtkWidget *button, 5751 SummaryView *summaryview) 5752 { 5753 summary_sort_by_column_click(summaryview, SORT_BY_SUBJECT); 5754 } 5755 5756 static void summary_score_clicked(GtkWidget *button, 5757 SummaryView *summaryview) 5758 { 5759 summary_sort_by_column_click(summaryview, SORT_BY_SCORE); 5760 } 5761 5762 static void summary_locked_clicked(GtkWidget *button, 5763 SummaryView *summaryview) 5764 { 5765 summary_sort_by_column_click(summaryview, SORT_BY_LOCKED); 5766 } 5767 5768 /* custom compare functions for sorting */ 5769 5770 static gint summary_cmp_by_date(GtkCMCList *clist, 5771 gconstpointer ptr1, gconstpointer ptr2) 5772 { 5773 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5774 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5775 gint res; 5776 if (!msginfo1 || !msginfo2) 5777 return -1; 5778 5779 res = (msginfo1->date_t - msginfo2->date_t); 5780 if (res == 0) 5781 res = msginfo1->msgnum - msginfo2->msgnum; 5782 return res; 5783 } 5784 5785 #define CMP_FUNC_DEF(func_name, val) \ 5786 static gint func_name(GtkCMCList *clist, \ 5787 gconstpointer ptr1, gconstpointer ptr2) \ 5788 { \ 5789 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; \ 5790 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; \ 5791 gint res; \ 5792 if (!msginfo1 || !msginfo2) \ 5793 return -1; \ 5794 \ 5795 res = (val); \ 5796 return (res != 0) ? res:summary_cmp_by_date(clist, ptr1, ptr2); \ 5797 } 5798 5799 CMP_FUNC_DEF(summary_cmp_by_mark, 5800 MSG_IS_MARKED(msginfo1->flags) - MSG_IS_MARKED(msginfo2->flags)) 5801 CMP_FUNC_DEF(summary_cmp_by_status, 5802 (-(MSG_IS_SPAM(msginfo1->flags))+(MSG_IS_UNREAD(msginfo1->flags)<<1)+(MSG_IS_NEW(msginfo1->flags)<<2)) 5803 - (-(MSG_IS_SPAM(msginfo2->flags))+(MSG_IS_UNREAD(msginfo2->flags)<<1)+(MSG_IS_NEW(msginfo2->flags)<<2)) ) 5804 CMP_FUNC_DEF(summary_cmp_by_mime, 5805 MSG_IS_WITH_ATTACHMENT(msginfo1->flags) - MSG_IS_WITH_ATTACHMENT(msginfo2->flags)) 5806 CMP_FUNC_DEF(summary_cmp_by_label, 5807 MSG_GET_COLORLABEL(msginfo1->flags) - 5808 MSG_GET_COLORLABEL(msginfo2->flags)) 5809 CMP_FUNC_DEF(summary_cmp_by_locked, 5810 MSG_IS_LOCKED(msginfo1->flags) - MSG_IS_LOCKED(msginfo2->flags)) 5811 5812 CMP_FUNC_DEF(summary_cmp_by_num, msginfo1->msgnum - msginfo2->msgnum) 5813 CMP_FUNC_DEF(summary_cmp_by_size, msginfo1->size - msginfo2->size) 5814 5815 #undef CMP_FUNC_DEF 5816 5817 static gint summary_cmp_by_subject(GtkCMCList *clist, 5818 gconstpointer ptr1, 5819 gconstpointer ptr2) 5820 { 5821 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5822 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5823 gint res; 5824 5825 if (!msginfo1->subject) 5826 return (msginfo2->subject != NULL); 5827 if (!msginfo2->subject) 5828 return -1; 5829 5830 res = subject_compare_for_sort 5831 (msginfo1->subject, msginfo2->subject); 5832 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2); 5833 } 5834 5835 static gint summary_cmp_by_thread_date(GtkCMCList *clist, 5836 gconstpointer ptr1, 5837 gconstpointer ptr2) 5838 { 5839 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5840 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5841 gint thread_diff = msginfo1->thread_date - msginfo2->thread_date; 5842 5843 if (msginfo1->thread_date > 0 && msginfo2->thread_date > 0) 5844 return thread_diff; 5845 else 5846 return msginfo1->date_t - msginfo2->date_t; 5847 } 5848 5849 static gint summary_cmp_by_from(GtkCMCList *clist, gconstpointer ptr1, 5850 gconstpointer ptr2) 5851 { 5852 const gchar *str1, *str2; 5853 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1; 5854 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2; 5855 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5856 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5857 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview"); 5858 gint res; 5859 5860 cm_return_val_if_fail(sv, -1); 5861 if (sv->col_state[sv->col_pos[S_COL_FROM]].visible) { 5862 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_FROM]])->text; 5863 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_FROM]])->text; 5864 } else { 5865 str1 = msginfo1->from; 5866 str2 = msginfo2->from; 5867 } 5868 5869 if (!str1) 5870 return str2 != NULL; 5871 5872 if (!str2) 5873 return -1; 5874 5875 res = g_utf8_collate(str1, str2); 5876 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2); 5877 } 5878 5879 static gint summary_cmp_by_to(GtkCMCList *clist, gconstpointer ptr1, 5880 gconstpointer ptr2) 5881 { 5882 const gchar *str1, *str2; 5883 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1; 5884 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2; 5885 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5886 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5887 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview"); 5888 gint res; 5889 cm_return_val_if_fail(sv, -1); 5890 5891 if (sv->col_state[sv->col_pos[S_COL_TO]].visible) { 5892 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_TO]])->text; 5893 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_TO]])->text; 5894 } else { 5895 str1 = msginfo1->to; 5896 str2 = msginfo2->to; 5897 } 5898 5899 if (!str1) 5900 return str2 != NULL; 5901 5902 if (!str2) 5903 return -1; 5904 5905 res = g_utf8_collate(str1, str2); 5906 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2); 5907 } 5908 5909 static gint summary_cmp_by_simplified_subject 5910 (GtkCMCList *clist, gconstpointer ptr1, gconstpointer ptr2) 5911 { 5912 const FolderItemPrefs *prefs; 5913 const gchar *str1, *str2; 5914 const GtkCMCListRow *r1 = (const GtkCMCListRow *) ptr1; 5915 const GtkCMCListRow *r2 = (const GtkCMCListRow *) ptr2; 5916 const MsgInfo *msginfo1 = r1->data; 5917 const MsgInfo *msginfo2 = r2->data; 5918 const SummaryView *sv = g_object_get_data(G_OBJECT(clist), "summaryview"); 5919 gint res; 5920 5921 cm_return_val_if_fail(sv, -1); 5922 cm_return_val_if_fail(msginfo1 != NULL && msginfo2 != NULL, -1); 5923 5924 if (sv->col_state[sv->col_pos[S_COL_SUBJECT]].visible) { 5925 str1 = GTK_CMCELL_TEXT(r1->cell[sv->col_pos[S_COL_SUBJECT]])->text; 5926 str2 = GTK_CMCELL_TEXT(r2->cell[sv->col_pos[S_COL_SUBJECT]])->text; 5927 } else { 5928 str1 = msginfo1->subject; 5929 str2 = msginfo2->subject; 5930 } 5931 5932 if (!str1) 5933 return str2 != NULL; 5934 5935 if (!str2) 5936 return -1; 5937 5938 prefs = msginfo1->folder->prefs; 5939 if (!prefs) 5940 prefs = msginfo2->folder->prefs; 5941 if (!prefs) 5942 return -1; 5943 5944 res = subject_compare_for_sort(str1, str2); 5945 return (res != 0)? res: summary_cmp_by_date(clist, ptr1, ptr2); 5946 } 5947 5948 static gint summary_cmp_by_score(GtkCMCList *clist, 5949 gconstpointer ptr1, gconstpointer ptr2) 5950 { 5951 MsgInfo *msginfo1 = ((GtkCMCListRow *)ptr1)->data; 5952 MsgInfo *msginfo2 = ((GtkCMCListRow *)ptr2)->data; 5953 int diff; 5954 5955 /* if score are equal, sort by date */ 5956 5957 diff = msginfo1->score - msginfo2->score; 5958 if (diff != 0) 5959 return diff; 5960 else 5961 return summary_cmp_by_date(clist, ptr1, ptr2); 5962 } 5963 5964 static void summary_ignore_thread_func_mark_unread(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 5965 { 5966 MsgInfo *msginfo; 5967 5968 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 5969 cm_return_if_fail(msginfo); 5970 5971 summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0); 5972 summary_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0); 5973 5974 debug_print("Message %d is marked as ignore thread\n", msginfo->msgnum); 5975 } 5976 5977 static void summary_ignore_thread_func_set_row(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 5978 { 5979 SummaryView *summaryview = (SummaryView *) data; 5980 MsgInfo *msginfo; 5981 5982 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 5983 cm_return_if_fail(msginfo); 5984 5985 summary_set_row_marks(summaryview, row); 5986 debug_print("Message %d update in row view\n", msginfo->msgnum); 5987 } 5988 5989 void summary_ignore_thread(SummaryView *summaryview) 5990 { 5991 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 5992 GList *cur; 5993 gboolean froze = FALSE; 5994 5995 START_LONG_OPERATION(summaryview, FALSE); 5996 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 5997 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 5998 GTK_CMCTREE_FUNC(summary_ignore_thread_func_mark_unread), 5999 summaryview); 6000 6001 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6002 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6003 GTK_CMCTREE_FUNC(summary_ignore_thread_func_set_row), 6004 summaryview); 6005 6006 END_LONG_OPERATION(summaryview); 6007 6008 summary_status_show(summaryview); 6009 } 6010 6011 static void summary_unignore_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 6012 { 6013 SummaryView *summaryview = (SummaryView *) data; 6014 MsgInfo *msginfo; 6015 6016 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 6017 cm_return_if_fail(msginfo); 6018 6019 summary_msginfo_unset_flags(msginfo, MSG_IGNORE_THREAD, 0); 6020 6021 summary_set_row_marks(summaryview, row); 6022 debug_print("Message %d is marked as unignore thread\n", 6023 msginfo->msgnum); 6024 } 6025 6026 void summary_unignore_thread(SummaryView *summaryview) 6027 { 6028 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 6029 GList *cur; 6030 gboolean froze = FALSE; 6031 6032 START_LONG_OPERATION(summaryview, FALSE); 6033 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6034 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6035 GTK_CMCTREE_FUNC(summary_unignore_thread_func), 6036 summaryview); 6037 6038 END_LONG_OPERATION(summaryview); 6039 6040 summary_status_show(summaryview); 6041 } 6042 6043 static void summary_check_ignore_thread_func 6044 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 6045 { 6046 MsgInfo *msginfo; 6047 gint *found_ignore = (gint *) data; 6048 6049 if (*found_ignore) return; 6050 else { 6051 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 6052 *found_ignore = msginfo && MSG_IS_IGNORE_THREAD(msginfo->flags); 6053 } 6054 } 6055 6056 void summary_toggle_ignore_thread(SummaryView *summaryview) 6057 { 6058 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 6059 GList *cur; 6060 gint found_ignore = 0; 6061 6062 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6063 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6064 GTK_CMCTREE_FUNC(summary_check_ignore_thread_func), 6065 &found_ignore); 6066 6067 if (found_ignore) 6068 summary_unignore_thread(summaryview); 6069 else 6070 summary_ignore_thread(summaryview); 6071 } 6072 6073 static void summary_watch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 6074 { 6075 SummaryView *summaryview = (SummaryView *) data; 6076 MsgInfo *msginfo; 6077 6078 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 6079 cm_return_if_fail(msginfo); 6080 6081 summary_msginfo_change_flags(msginfo, MSG_WATCH_THREAD, 0, MSG_IGNORE_THREAD, 0); 6082 6083 summary_set_row_marks(summaryview, row); 6084 debug_print("Message %d is marked as watch thread\n", 6085 msginfo->msgnum); 6086 } 6087 6088 void summary_watch_thread(SummaryView *summaryview) 6089 { 6090 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 6091 GList *cur; 6092 gboolean froze = FALSE; 6093 6094 START_LONG_OPERATION(summaryview, FALSE); 6095 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6096 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6097 GTK_CMCTREE_FUNC(summary_watch_thread_func), 6098 summaryview); 6099 6100 END_LONG_OPERATION(summaryview); 6101 6102 summary_status_show(summaryview); 6103 } 6104 6105 static void summary_unwatch_thread_func(GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 6106 { 6107 SummaryView *summaryview = (SummaryView *) data; 6108 MsgInfo *msginfo; 6109 6110 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 6111 cm_return_if_fail(msginfo); 6112 6113 summary_msginfo_unset_flags(msginfo, MSG_WATCH_THREAD, 0); 6114 6115 summary_set_row_marks(summaryview, row); 6116 debug_print("Message %d is marked as unwatch thread\n", 6117 msginfo->msgnum); 6118 } 6119 6120 void summary_unwatch_thread(SummaryView *summaryview) 6121 { 6122 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 6123 GList *cur; 6124 gboolean froze = FALSE; 6125 6126 START_LONG_OPERATION(summaryview, FALSE); 6127 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6128 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6129 GTK_CMCTREE_FUNC(summary_unwatch_thread_func), 6130 summaryview); 6131 6132 END_LONG_OPERATION(summaryview); 6133 6134 summary_status_show(summaryview); 6135 } 6136 6137 static void summary_check_watch_thread_func 6138 (GtkCMCTree *ctree, GtkCMCTreeNode *row, gpointer data) 6139 { 6140 MsgInfo *msginfo; 6141 gint *found_watch = (gint *) data; 6142 6143 if (*found_watch) return; 6144 else { 6145 msginfo = gtk_cmctree_node_get_row_data(ctree, row); 6146 *found_watch = msginfo && MSG_IS_WATCH_THREAD(msginfo->flags); 6147 } 6148 } 6149 6150 void summary_toggle_watch_thread(SummaryView *summaryview) 6151 { 6152 GtkCMCTree *ctree = GTK_CMCTREE(summaryview->ctree); 6153 GList *cur; 6154 gint found_watch = 0; 6155 6156 for (cur = GTK_CMCLIST(ctree)->selection; cur != NULL && cur->data != NULL; cur = cur->next) 6157 gtk_cmctree_pre_recursive(ctree, GTK_CMCTREE_NODE(cur->data), 6158 GTK_CMCTREE_FUNC(summary_check_watch_thread_func), 6159 &found_watch); 6160 6161 if (found_watch) 6162 summary_unwatch_thread(summaryview); 6163 else 6164 summary_watch_thread(summaryview); 6165 } 6166 6167 6168 void summary_toggle_show_read_messages(SummaryView *summaryview) 6169 { 6170 FolderItemUpdateData source; 6171 if (summaryview->folder_item->hide_read_msgs) 6172 summaryview->folder_item->hide_read_msgs = 0; 6173 else 6174 summaryview->folder_item->hide_read_msgs = 1; 6175 6176 source.item = summaryview->folder_item; 6177 source.update_flags = F_ITEM_UPDATE_NAME; 6178 source.msg = NULL; 6179 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); 6180 summary_show(summaryview, summaryview->folder_item, FALSE); 6181 } 6182 6183 void summary_toggle_show_del_messages(SummaryView *summaryview) 6184 { 6185 FolderItemUpdateData source; 6186 if (summaryview->folder_item->hide_del_msgs) 6187 summaryview->folder_item->hide_del_msgs = 0; 6188 else 6189 summaryview->folder_item->hide_del_msgs = 1; 6190 6191 source.item = summaryview->folder_item; 6192 source.update_flags = F_ITEM_UPDATE_NAME; 6193 source.msg = NULL; 6194 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); 6195 summary_show(summaryview, summaryview->folder_item, FALSE); 6196 } 6197 6198 void summary_toggle_show_read_threads(SummaryView *summaryview) 6199 { 6200 FolderItemUpdateData source; 6201 if (summaryview->folder_item->hide_read_threads) 6202 summaryview->folder_item->hide_read_threads = 0; 6203 else 6204 summaryview->folder_item->hide_read_threads = 1; 6205 6206 source.item = summaryview->folder_item; 6207 source.update_flags = F_ITEM_UPDATE_NAME; 6208 source.msg = NULL; 6209 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); 6210 summary_show(summaryview, summaryview->folder_item, FALSE); 6211 } 6212 6213 static void summary_set_hide_menu (SummaryView *summaryview, 6214 const gchar *menu_item, 6215 guint action) 6216 { 6217 GtkWidget *widget; 6218 6219 widget = gtk_ui_manager_get_widget(summaryview->mainwin->ui_manager, menu_item); 6220 cm_return_if_fail(widget != NULL); 6221 6222 g_object_set_data(G_OBJECT(widget), "dont_toggle", 6223 GINT_TO_POINTER(1)); 6224 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(widget), action); 6225 g_object_set_data(G_OBJECT(widget), "dont_toggle", 6226 GINT_TO_POINTER(0)); 6227 } 6228 6229 void summary_reflect_prefs_pixmap_theme(SummaryView *summaryview) 6230 { 6231 stock_pixbuf_gdk(STOCK_PIXMAP_MARK, &markxpm); 6232 stock_pixbuf_gdk(STOCK_PIXMAP_DELETED, &deletedxpm); 6233 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm); 6234 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm); 6235 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED, &repliedxpm); 6236 stock_pixbuf_gdk(STOCK_PIXMAP_FORWARDED, &forwardedxpm); 6237 stock_pixbuf_gdk(STOCK_PIXMAP_REPLIED_AND_FORWARDED, &repliedandforwardedxpm); 6238 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP, &clipxpm); 6239 stock_pixbuf_gdk(STOCK_PIXMAP_LOCKED, &lockedxpm); 6240 stock_pixbuf_gdk(STOCK_PIXMAP_IGNORETHREAD, &ignorethreadxpm); 6241 stock_pixbuf_gdk(STOCK_PIXMAP_WATCHTHREAD, &watchthreadxpm); 6242 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_KEY, &clipkeyxpm); 6243 stock_pixbuf_gdk(STOCK_PIXMAP_KEY, &keyxpm); 6244 stock_pixbuf_gdk(STOCK_PIXMAP_KEY_SIGN, &keysignxpm); 6245 stock_pixbuf_gdk(STOCK_PIXMAP_GPG_SIGNED, &gpgsignedxpm); 6246 stock_pixbuf_gdk(STOCK_PIXMAP_CLIP_GPG_SIGNED, &clipgpgsignedxpm); 6247 stock_pixbuf_gdk(STOCK_PIXMAP_SPAM, &spamxpm); 6248 stock_pixbuf_gdk(STOCK_PIXMAP_MOVED, &movedxpm); 6249 stock_pixbuf_gdk(STOCK_PIXMAP_COPIED, &copiedxpm); 6250 6251 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN); 6252 6253 folderview_unselect(summaryview->folderview); 6254 folderview_select(summaryview->folderview, summaryview->folder_item); 6255 summary_set_column_titles(summaryview); 6256 } 6257 6258 static regex_t *summary_compile_simplify_regexp(gchar *simplify_subject_regexp) 6259 { 6260 int err; 6261 gchar buf[BUFFSIZE]; 6262 regex_t *preg = NULL; 6263 6264 preg = g_new0(regex_t, 1); 6265 6266 err = regcomp(preg, simplify_subject_regexp, REG_EXTENDED); 6267 if (err) { 6268 regerror(err, preg, buf, BUFFSIZE); 6269 alertpanel_error("Regular expression error:\n%s", buf); 6270 regfree(preg); 6271 g_free(preg); 6272 preg = NULL; 6273 } 6274 6275 return preg; 6276 } 6277 void summary_set_prefs_from_folderitem(SummaryView *summaryview, FolderItem *item) 6278 { 6279 FolderSortKey sort_key; 6280 FolderSortType sort_type; 6281 cm_return_if_fail(summaryview != NULL); 6282 cm_return_if_fail(item != NULL); 6283 6284 /* Subject simplification */ 6285 if(summaryview->simplify_subject_preg) { 6286 regfree(summaryview->simplify_subject_preg); 6287 g_free(summaryview->simplify_subject_preg); 6288 summaryview->simplify_subject_preg = NULL; 6289 } 6290 if(item->prefs && item->prefs->simplify_subject_regexp && 6291 item->prefs->simplify_subject_regexp[0] && item->prefs->enable_simplify_subject) 6292 summaryview->simplify_subject_preg = summary_compile_simplify_regexp(item->prefs->simplify_subject_regexp); 6293 6294 /* Sorting */ 6295 sort_key = item->sort_key; 6296 sort_type = item->sort_type; 6297 6298 folder_get_sort_type(item->folder, &sort_key, &sort_type); 6299 6300 summaryview->sort_key = sort_key; 6301 summaryview->sort_type = sort_type; 6302 6303 /* Threading */ 6304 summaryview->threaded = item->threaded; 6305 summaryview->thread_collapsed = item->thread_collapsed; 6306 6307 /* Scoring */ 6308 } 6309 6310 void summary_save_prefs_to_folderitem(SummaryView *summaryview, FolderItem *item) 6311 { 6312 /* Sorting */ 6313 item->sort_key = summaryview->sort_key; 6314 item->sort_type = summaryview->sort_type; 6315 6316 /* Threading */ 6317 item->threaded = summaryview->threaded; 6318 item->thread_collapsed = summaryview->thread_collapsed; 6319 } 6320 6321 static gboolean summary_update_msg(gpointer source, gpointer data) 6322 { 6323 MsgInfoUpdate *msginfo_update = (MsgInfoUpdate *) source; 6324 SummaryView *summaryview = (SummaryView *)data; 6325 GtkCMCTreeNode *node; 6326 6327 cm_return_val_if_fail(msginfo_update != NULL, TRUE); 6328 cm_return_val_if_fail(summaryview != NULL, FALSE); 6329 6330 if (msginfo_update->msginfo->folder != summaryview->folder_item) 6331 return FALSE; 6332 6333 if (msginfo_update->flags & MSGINFO_UPDATE_FLAGS) { 6334 node = gtk_cmctree_find_by_row_data( 6335 GTK_CMCTREE(summaryview->ctree), NULL, 6336 msginfo_update->msginfo); 6337 6338 if (node) 6339 summary_set_row_marks(summaryview, node); 6340 } 6341 6342 return FALSE; 6343 } 6344 6345 void summary_update_unread(SummaryView *summaryview, FolderItem *removed_item) 6346 { 6347 static gboolean tips_initialized = FALSE; 6348 6349 if (!tips_initialized) 6350 return; 6351 summary_set_folder_pixmap(summaryview, STOCK_PIXMAP_DIR_OPEN); 6352 CLAWS_SET_TIP(summaryview->folder_pixmap_eventbox, NULL); 6353 tips_initialized = FALSE; 6354 } 6355 6356 static gboolean summary_update_folder_item_hook(gpointer source, gpointer data) 6357 { 6358 FolderItemUpdateData *hookdata = (FolderItemUpdateData *)source; 6359 SummaryView *summaryview = (SummaryView *)data; 6360 6361 cm_return_val_if_fail(hookdata != NULL, FALSE); 6362 cm_return_val_if_fail(hookdata->item != NULL, FALSE); 6363 cm_return_val_if_fail(summaryview != NULL, FALSE); 6364 6365 if (hookdata->item == summaryview->folder_item && 6366 hookdata->update_flags & F_ITEM_UPDATE_NAME) { 6367 gchar *name = folder_item_get_name(hookdata->item); 6368 gtk_label_set_text(GTK_LABEL(summaryview->statlabel_folder), name); 6369 g_free(name); 6370 } 6371 summary_update_unread(summaryview, NULL); 6372 6373 return FALSE; 6374 } 6375 6376 static gboolean summary_update_folder_hook(gpointer source, gpointer data) 6377 { 6378 FolderUpdateData *hookdata; 6379 SummaryView *summaryview = (SummaryView *)data; 6380 hookdata = source; 6381 if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) { 6382 summary_update_unread(summaryview, hookdata->item); 6383 } else 6384 summary_update_unread(summaryview, NULL); 6385 6386 return FALSE; 6387 } 6388 6389 static void summary_reedit_cb(GtkAction *gaction, gpointer data) 6390 { 6391 SummaryView *summaryview = (SummaryView *)data; 6392 summary_reedit(summaryview); 6393 } 6394 6395 #define DO_ACTION(name, act) { if (!strcmp(a_name, name)) action = act; } 6396 static void summary_reply_cb(GtkAction *gaction, gpointer data) 6397 { 6398 SummaryView *summaryview = (SummaryView *)data; 6399 GSList *msginfo_list = NULL; 6400 gint action = COMPOSE_REPLY; 6401 const gchar *a_name = gtk_action_get_name(gaction); 6402 6403 DO_ACTION("SummaryViewPopup/Reply", COMPOSE_REPLY); 6404 DO_ACTION("SummaryViewPopup/ReplyTo/All", COMPOSE_REPLY_TO_ALL); 6405 DO_ACTION("SummaryViewPopup/ReplyTo/Sender", COMPOSE_REPLY_TO_SENDER); 6406 DO_ACTION("SummaryViewPopup/ReplyTo/List", COMPOSE_REPLY_TO_LIST); 6407 DO_ACTION("SummaryViewPopup/Forward", COMPOSE_FORWARD_INLINE); 6408 DO_ACTION("SummaryViewPopup/ForwardAtt", COMPOSE_FORWARD_AS_ATTACH); 6409 DO_ACTION("SummaryViewPopup/Redirect", COMPOSE_REDIRECT); 6410 6411 msginfo_list = summary_get_selection(summaryview); 6412 cm_return_if_fail(msginfo_list != NULL); 6413 compose_reply_from_messageview(NULL, msginfo_list, action); 6414 g_slist_free(msginfo_list); 6415 } 6416 6417 gboolean summary_is_opened_message_selected(SummaryView *summaryview) 6418 { 6419 GList *sel = NULL; 6420 6421 cm_return_val_if_fail(summaryview != NULL, FALSE); 6422 6423 sel = GTK_CMCLIST(summaryview->ctree)->selection; 6424 6425 if (summaryview->displayed == NULL || sel == NULL) { 6426 return FALSE; 6427 } 6428 6429 for ( ; sel != NULL; sel = sel->next) { 6430 if (summaryview->displayed == GTK_CMCTREE_NODE(sel->data)) { 6431 return TRUE; 6432 } 6433 } 6434 return FALSE; 6435 } 6436 6437 gboolean summary_has_opened_message(SummaryView *summaryview) 6438 { 6439 cm_return_val_if_fail(summaryview != NULL, FALSE); 6440 6441 return (summaryview->displayed != NULL); 6442 } 6443 6444 static void summary_header_lock_sorting_cb(GtkAction *gaction, gpointer data) 6445 { 6446 SummaryView *summaryview = (SummaryView *)data; 6447 gboolean sorting_lock = prefs_common_get_prefs()->summary_col_lock; 6448 6449 if (summaryview->header_menu_lock) 6450 return; 6451 6452 debug_print("%slocking summaryview columns\n", 6453 sorting_lock ? "un" : ""); 6454 prefs_common_get_prefs()->summary_col_lock = !sorting_lock; 6455 } 6456 6457 static void summary_header_set_displayed_columns_cb(GtkAction *gaction, 6458 gpointer data) 6459 { 6460 SummaryView *summaryview = (SummaryView *)data; 6461 6462 if (summaryview->header_menu_lock) 6463 return; 6464 6465 prefs_summary_column_open(); 6466 }