folderview.c (81259B)
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 #include <stdio.h> 26 #include <string.h> 27 #include <stdlib.h> 28 29 #include "main.h" 30 #include "mainwindow.h" 31 #include "folderview.h" 32 #include "summaryview.h" 33 #include "inputdialog.h" 34 #include "manage_window.h" 35 #include "alertpanel.h" 36 #include "menu.h" 37 #include "stock_pixmap.h" 38 #include "procmsg.h" 39 #include "utils.h" 40 #include "gtkutils.h" 41 #include "prefs_common.h" 42 #include "prefs_account.h" 43 #include "prefs_folder_item.h" 44 #include "account.h" 45 #include "folder.h" 46 #include "foldersel.h" 47 #include "inc.h" 48 #include "statusbar.h" 49 #include "hooks.h" 50 #include "folderutils.h" 51 #include "prefs_folder_column.h" 52 #include "manual.h" 53 #include "log.h" 54 #include "gtkcmctree.h" 55 56 #define COL_FOLDER_WIDTH 150 57 #define COL_NUM_WIDTH 32 58 59 static GList *folderview_list = NULL; 60 61 static GtkStyle *bold_style; 62 63 static GdkPixbuf *inboxxpm; 64 static GdkPixbuf *inboxhrmxpm; 65 static GdkPixbuf *inboxopenxpm; 66 static GdkPixbuf *inboxopenhrmxpm; 67 static GdkPixbuf *outboxxpm; 68 static GdkPixbuf *outboxhrmxpm; 69 static GdkPixbuf *outboxopenxpm; 70 static GdkPixbuf *outboxopenhrmxpm; 71 static GdkPixbuf *folderxpm; 72 static GdkPixbuf *folderhrmxpm; 73 static GdkPixbuf *folderopenxpm; 74 static GdkPixbuf *folderopenhrmxpm; 75 static GdkPixbuf *trashopenxpm; 76 static GdkPixbuf *trashopenhrmxpm; 77 static GdkPixbuf *trashxpm; 78 static GdkPixbuf *trashhrmxpm; 79 static GdkPixbuf *queuexpm; 80 static GdkPixbuf *queuehrmxpm; 81 static GdkPixbuf *queueopenxpm; 82 static GdkPixbuf *queueopenhrmxpm; 83 static GdkPixbuf *draftsxpm; 84 static GdkPixbuf *draftsopenxpm; 85 static GdkPixbuf *foldersubsxpm; 86 static GdkPixbuf *foldersubsopenxpm; 87 static GdkPixbuf *foldernoselectxpm; 88 static GdkPixbuf *foldernoselectopenxpm; 89 90 static GdkPixbuf *m_inboxxpm; 91 static GdkPixbuf *m_inboxhrmxpm; 92 static GdkPixbuf *m_inboxopenxpm; 93 static GdkPixbuf *m_inboxopenhrmxpm; 94 static GdkPixbuf *m_outboxxpm; 95 static GdkPixbuf *m_outboxhrmxpm; 96 static GdkPixbuf *m_outboxopenxpm; 97 static GdkPixbuf *m_outboxopenhrmxpm; 98 static GdkPixbuf *m_folderxpm; 99 static GdkPixbuf *m_folderhrmxpm; 100 static GdkPixbuf *m_folderopenxpm; 101 static GdkPixbuf *m_folderopenhrmxpm; 102 static GdkPixbuf *m_trashopenxpm; 103 static GdkPixbuf *m_trashopenhrmxpm; 104 static GdkPixbuf *m_trashxpm; 105 static GdkPixbuf *m_trashhrmxpm; 106 static GdkPixbuf *m_queuexpm; 107 static GdkPixbuf *m_queuehrmxpm; 108 static GdkPixbuf *m_queueopenxpm; 109 static GdkPixbuf *m_queueopenhrmxpm; 110 static GdkPixbuf *m_draftsxpm; 111 static GdkPixbuf *m_draftsopenxpm; 112 static GdkPixbuf *m_foldersubsxpm; 113 static GdkPixbuf *m_foldernoselectxpm; 114 115 static GdkPixbuf *newxpm; 116 static GdkPixbuf *unreadxpm; 117 static GdkPixbuf *readxpm; 118 119 static void folderview_select_node (FolderView *folderview, 120 GtkCMCTreeNode *node); 121 static void folderview_set_folders (FolderView *folderview); 122 static void folderview_sort_folders (FolderView *folderview, 123 GtkCMCTreeNode *root, 124 Folder *folder); 125 static void folderview_append_folder (FolderView *folderview, 126 Folder *folder); 127 static void folderview_update_node (FolderView *folderview, 128 GtkCMCTreeNode *node); 129 130 static gint folderview_clist_compare (GtkCMCList *clist, 131 gconstpointer ptr1, 132 gconstpointer ptr2); 133 134 /* callback functions */ 135 static gboolean folderview_button_pressed (GtkWidget *ctree, 136 GdkEventButton *event, 137 FolderView *folderview); 138 static gboolean folderview_button_released (GtkWidget *ctree, 139 GdkEventButton *event, 140 FolderView *folderview); 141 static gboolean folderview_key_pressed (GtkWidget *widget, 142 GdkEventKey *event, 143 FolderView *folderview); 144 static void folderview_selected (GtkCMCTree *ctree, 145 GtkCMCTreeNode *row, 146 gint column, 147 FolderView *folderview); 148 static void folderview_tree_expanded (GtkCMCTree *ctree, 149 GtkCMCTreeNode *node, 150 FolderView *folderview); 151 static void folderview_tree_collapsed (GtkCMCTree *ctree, 152 GtkCMCTreeNode *node, 153 FolderView *folderview); 154 static void folderview_popup_close (GtkMenuShell *menu_shell, 155 FolderView *folderview); 156 static void folderview_col_resized (GtkCMCList *clist, 157 gint column, 158 gint width, 159 FolderView *folderview); 160 161 static void mark_all_read_unread_handler (GtkAction *action, 162 gpointer data, 163 gboolean recursive, 164 gboolean read); 165 166 static void mark_all_read_cb (GtkAction *action, 167 gpointer data); 168 static void mark_all_unread_cb (GtkAction *action, 169 gpointer data); 170 static void mark_all_read_recursive_cb (GtkAction *action, 171 gpointer data); 172 static void mark_all_unread_recursive_cb (GtkAction *action, 173 gpointer data); 174 175 static void folderview_empty_trash_cb (GtkAction *action, 176 gpointer data); 177 178 static void folderview_send_queue_cb (GtkAction *action, 179 gpointer data); 180 181 static void folderview_startup_folder_cb(GtkAction *action, 182 gpointer data); 183 184 static void folderview_property_cb (GtkAction *action, 185 gpointer data); 186 187 static void folderview_create_folder_node (FolderView *folderview, 188 FolderItem *item); 189 static gboolean folderview_update_folder (gpointer source, 190 gpointer userdata); 191 static gboolean folderview_update_item_claws (gpointer source, 192 gpointer data); 193 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row, 194 GdkEventButton *event); 195 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction, 196 gpointer data); 197 static gboolean folderview_header_button_pressed(GtkWidget *widget, 198 GdkEvent *_event, 199 gpointer user_data); 200 201 GHashTable *folderview_popups; 202 203 static GtkActionEntry folderview_common_popup_entries[] = 204 { 205 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL}, 206 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) }, 207 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) }, 208 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) }, 209 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) }, 210 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL}, 211 {"FolderViewPopup/OpenFolder", NULL, N_("Open on start-up"), NULL, NULL, G_CALLBACK(folderview_startup_folder_cb) }, 212 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) }, 213 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) }, 214 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) }, 215 216 }; 217 218 static GtkActionEntry folderview_header_popup_entries[] = 219 { 220 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL }, 221 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) } 222 }; 223 224 void folderview_initialize(void) 225 { 226 FolderViewPopup *fpopup; 227 228 fpopup = g_new0(FolderViewPopup, 1); 229 230 fpopup->klass = "common"; 231 fpopup->path = "<CommonFolder>"; 232 fpopup->entries = folderview_common_popup_entries; 233 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries); 234 fpopup->set_sensitivity = NULL; 235 236 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal); 237 g_hash_table_insert(folderview_popups, "common", fpopup); 238 } 239 240 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup) 241 { 242 FolderViewPopup *fpopup_common; 243 GtkActionGroup *action_group; 244 245 action_group = cm_menu_create_action_group( 246 fpopup->path, 247 fpopup->entries, fpopup->n_entries, 248 (gpointer)folderview); 249 250 if (fpopup->toggle_entries) 251 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries, 252 fpopup->n_toggle_entries, 253 (gpointer)folderview); 254 if (fpopup->radio_entries) 255 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries, 256 fpopup->n_radio_entries, fpopup->radio_default, 257 G_CALLBACK(fpopup->radio_callback), 258 (gpointer)folderview); 259 260 fpopup_common = g_hash_table_lookup(folderview_popups, "common"); 261 if (fpopup_common != fpopup) { 262 gtk_action_group_add_actions(action_group, fpopup_common->entries, 263 fpopup_common->n_entries, 264 (gpointer)folderview); 265 if (fpopup_common->toggle_entries) 266 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries, 267 fpopup_common->n_toggle_entries, 268 (gpointer)folderview); 269 if (fpopup_common->radio_entries) 270 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries, 271 fpopup_common->n_radio_entries, fpopup_common->radio_default, 272 G_CALLBACK(fpopup_common->radio_callback), 273 (gpointer)folderview); 274 } 275 276 return action_group; 277 } 278 279 static void create_action_groups(gpointer key, gpointer value, gpointer data) 280 { 281 FolderView *folderview = data; 282 FolderViewPopup *fpopup = value; 283 GtkActionGroup *group; 284 285 group = create_action_group(folderview, fpopup); 286 g_hash_table_insert(folderview->popups, fpopup->klass, group); 287 } 288 289 static void folderview_column_set_titles(FolderView *folderview) 290 { 291 GtkWidget *ctree = folderview->ctree; 292 GtkWidget *label_folder; 293 GtkWidget *label_new; 294 GtkWidget *label_unread; 295 GtkWidget *label_total; 296 GtkWidget *hbox_folder; 297 GtkWidget *hbox_new; 298 GtkWidget *hbox_unread; 299 GtkWidget *hbox_total; 300 gint *col_pos = folderview->col_pos; 301 302 debug_print("setting titles...\n"); 303 gtk_widget_realize(folderview->ctree); 304 gtk_widget_show_all(folderview->scrolledwin); 305 306 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps 307 * instead text (text overflows making them unreadable and ugly) */ 308 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm); 309 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm); 310 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm); 311 312 label_folder = gtk_label_new(_("Folder")); 313 label_new = gtk_image_new_from_pixbuf(newxpm); 314 label_unread = gtk_image_new_from_pixbuf(unreadxpm); 315 label_total = gtk_image_new_from_pixbuf(readxpm); 316 317 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree)); 318 319 hbox_folder = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); 320 hbox_new = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); 321 hbox_unread = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); 322 hbox_total = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); 323 324 /* left justified */ 325 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0); 326 gtk_widget_set_halign(label_folder, GTK_ALIGN_START); 327 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0); 328 gtk_widget_set_halign(label_new, GTK_ALIGN_END); 329 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0); 330 gtk_widget_set_halign(label_unread, GTK_ALIGN_END); 331 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0); 332 gtk_widget_set_halign(label_total, GTK_ALIGN_END); 333 334 gtk_widget_show_all(hbox_folder); 335 gtk_widget_show_all(hbox_new); 336 gtk_widget_show_all(hbox_unread); 337 gtk_widget_show_all(hbox_total); 338 339 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder); 340 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new); 341 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread); 342 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total); 343 344 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New")); 345 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread")); 346 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total")); 347 } 348 349 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data) 350 { 351 FolderView *folderview = (FolderView *)data; 352 GdkEventButton event; 353 if (folderview_get_selected_item(folderview) == NULL) 354 return FALSE; 355 356 event.button = 3; 357 event.time = gtk_get_current_event_time(); 358 359 folderview_set_sens_and_popup_menu(folderview, -1, 360 &event); 361 362 return TRUE; 363 } 364 365 366 static GtkWidget *folderview_ctree_create(FolderView *folderview) 367 { 368 GtkWidget *ctree; 369 gint *col_pos; 370 FolderColumnState *col_state; 371 FolderColumnType type; 372 gchar *titles[N_FOLDER_COLS]; 373 gint i; 374 GtkWidget *scrolledwin = folderview->scrolledwin; 375 376 debug_print("creating tree...\n"); 377 memset(titles, 0, sizeof(titles)); 378 379 col_state = prefs_folder_column_get_config(); 380 memset(titles, 0, sizeof(titles)); 381 382 col_pos = folderview->col_pos; 383 384 for (i = 0; i < N_FOLDER_COLS; i++) { 385 folderview->col_state[i] = col_state[i]; 386 type = col_state[i].type; 387 col_pos[type] = i; 388 } 389 390 titles[col_pos[F_COL_FOLDER]] = _("Folder"); 391 titles[col_pos[F_COL_NEW]] = _("New"); 392 titles[col_pos[F_COL_UNREAD]] = _("Unread"); 393 /* TRANSLATORS: This in Number sign in American style */ 394 titles[col_pos[F_COL_TOTAL]] = _("#"); 395 396 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER], 397 titles); 398 399 gtk_widget_set_name(GTK_WIDGET(ctree), "folderview_sctree"); 400 401 if (prefs_common.show_col_headers == FALSE) 402 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree)); 403 404 405 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE); 406 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW], 407 GTK_JUSTIFY_RIGHT); 408 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), 409 col_pos[F_COL_UNREAD], 410 GTK_JUSTIFY_RIGHT); 411 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), 412 col_pos[F_COL_TOTAL], 413 GTK_JUSTIFY_RIGHT); 414 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree), 415 GTK_CMCTREE_EXPANDER_TRIANGLE); 416 417 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries); 418 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE); 419 420 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT); 421 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare); 422 423 /* don't let title buttons take key focus */ 424 for (i = 0; i < N_FOLDER_COLS; i++) { 425 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE); 426 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i], 427 prefs_common.folder_col_size[i]); 428 gtk_cmclist_set_column_visibility 429 (GTK_CMCLIST(ctree), i, col_state[i].visible); 430 431 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button), 432 "button-press-event", 433 G_CALLBACK(folderview_header_button_pressed), 434 folderview); 435 } 436 437 g_signal_connect(G_OBJECT(ctree), "key_press_event", 438 G_CALLBACK(folderview_key_pressed), 439 folderview); 440 g_signal_connect(G_OBJECT(ctree), "button_press_event", 441 G_CALLBACK(folderview_button_pressed), 442 folderview); 443 g_signal_connect(G_OBJECT(ctree), "popup-menu", 444 G_CALLBACK(folderview_popup_menu), folderview); 445 g_signal_connect(G_OBJECT(ctree), "button_release_event", 446 G_CALLBACK(folderview_button_released), 447 folderview); 448 g_signal_connect(G_OBJECT(ctree), "tree_select_row", 449 G_CALLBACK(folderview_selected), folderview); 450 451 g_signal_connect_after(G_OBJECT(ctree), "tree_expand", 452 G_CALLBACK(folderview_tree_expanded), 453 folderview); 454 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse", 455 G_CALLBACK(folderview_tree_collapsed), 456 folderview); 457 458 g_signal_connect(G_OBJECT(ctree), "resize_column", 459 G_CALLBACK(folderview_col_resized), 460 folderview); 461 462 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree); 463 464 return ctree; 465 } 466 467 void folderview_set_column_order(FolderView *folderview) 468 { 469 GtkWidget *ctree = folderview->ctree; 470 FolderItem *item = folderview_get_selected_item(folderview); 471 FolderItem *sel_item = NULL, *op_item = NULL; 472 GtkWidget *scrolledwin = folderview->scrolledwin; 473 474 if (folderview->selected) 475 sel_item = folderview_get_selected_item(folderview); 476 if (folderview->opened) 477 op_item = folderview_get_opened_item(folderview); 478 479 debug_print("recreating tree...\n"); 480 gtk_widget_destroy(folderview->ctree); 481 482 483 folderview->ctree = ctree = folderview_ctree_create(folderview); 484 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 485 GTK_CMCLIST(ctree)->hadjustment); 486 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin), 487 GTK_CMCLIST(ctree)->vadjustment); 488 gtk_widget_show(ctree); 489 490 if (sel_item) 491 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item); 492 if (op_item) 493 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item); 494 495 folderview_set(folderview); 496 folderview_column_set_titles(folderview); 497 498 folderview_select(folderview,item); 499 } 500 501 FolderView *folderview_create(MainWindow *mainwin) 502 { 503 FolderView *folderview; 504 GtkWidget *scrolledwin; 505 GtkWidget *ctree; 506 507 debug_print("Creating folder view...\n"); 508 folderview = g_new0(FolderView, 1); 509 510 scrolledwin = gtk_scrolled_window_new(NULL, NULL); 511 gtk_widget_set_name(GTK_WIDGET(scrolledwin), "folderview"); 512 gtk_scrolled_window_set_policy 513 (GTK_SCROLLED_WINDOW(scrolledwin), 514 GTK_POLICY_AUTOMATIC, 515 prefs_common.folderview_vscrollbar_policy); 516 517 folderview->scrolledwin = scrolledwin; 518 ctree = folderview_ctree_create(folderview); 519 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0); 520 521 /* create popup factories */ 522 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal); 523 g_hash_table_foreach(folderview_popups, create_action_groups, folderview); 524 525 gtk_action_group_add_actions(mainwin->action_group, 526 folderview_header_popup_entries, 527 G_N_ELEMENTS(folderview_header_popup_entries), 528 (gpointer)folderview); 529 530 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU) 531 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM) 532 533 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM( 534 gtk_ui_manager_get_widget(mainwin->ui_manager, 535 "/Menus/FolderViewHeaderPopup") )); 536 537 folderview->ctree = ctree; 538 539 folderview->folder_update_callback_id = 540 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview); 541 folderview->folder_item_update_callback_id = 542 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview); 543 544 gtk_widget_show_all(scrolledwin); 545 546 folderview_list = g_list_append(folderview_list, folderview); 547 return folderview; 548 } 549 550 void folderview_init(FolderView *folderview) 551 { 552 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm); 553 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm); 554 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm); 555 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm); 556 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm); 557 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm); 558 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm); 559 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm); 560 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm); 561 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm); 562 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm); 563 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm); 564 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm); 565 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm); 566 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm); 567 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm); 568 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm); 569 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm); 570 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm); 571 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm); 572 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm); 573 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm); 574 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm); 575 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm); 576 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm); 577 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm); 578 579 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm); 580 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm); 581 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm); 582 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm); 583 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm); 584 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm); 585 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm); 586 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm); 587 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm); 588 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm); 589 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm); 590 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm); 591 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm); 592 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm); 593 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm); 594 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm); 595 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm); 596 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm); 597 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm); 598 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm); 599 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm); 600 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm); 601 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm); 602 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm); 603 } 604 605 static gboolean folderview_defer_set(gpointer data) 606 { 607 FolderView *folderview = (FolderView *)data; 608 MainWindow *mainwin = folderview->mainwin; 609 610 if (!mainwin) 611 return FALSE; 612 if (mainwin->lock_count) 613 return TRUE; 614 615 debug_print("doing deferred folderview_set now\n"); 616 folderview_set(folderview); 617 618 folderview->deferred_refresh_id = 0; 619 return FALSE; 620 } 621 622 void folderview_set(FolderView *folderview) 623 { 624 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 625 MainWindow *mainwin = folderview->mainwin; 626 FolderItem *sel_item = NULL, *op_item = NULL; 627 628 if (!mainwin) 629 return; 630 631 if (mainwin->lock_count) { 632 if (folderview->deferred_refresh_id == 0) 633 folderview->deferred_refresh_id = 634 g_timeout_add(500, folderview_defer_set, folderview); 635 debug_print("deferred folderview_set\n"); 636 return; 637 } 638 639 inc_lock(); 640 debug_print("Setting folder info...\n"); 641 STATUSBAR_PUSH(mainwin, _("Setting folder info...")); 642 643 main_window_cursor_wait(mainwin); 644 645 if (folderview->selected) 646 sel_item = folderview_get_selected_item(folderview); 647 if (folderview->opened) 648 op_item = folderview_get_opened_item(folderview); 649 650 folderview->selected = NULL; 651 folderview->opened = NULL; 652 653 gtk_cmclist_freeze(GTK_CMCLIST(ctree)); 654 gtk_cmclist_clear(GTK_CMCLIST(ctree)); 655 656 folderview_set_folders(folderview); 657 658 if (sel_item) 659 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item); 660 if (op_item) 661 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item); 662 663 gtk_cmclist_thaw(GTK_CMCLIST(ctree)); 664 main_window_cursor_normal(mainwin); 665 STATUSBAR_POP(mainwin); 666 inc_unlock(); 667 } 668 669 void folderview_set_all(void) 670 { 671 GList *list; 672 673 for (list = folderview_list; list != NULL; list = list->next) 674 folderview_set((FolderView *)list->data); 675 } 676 677 void folderview_select(FolderView *folderview, FolderItem *item) 678 { 679 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 680 GtkCMCTreeNode *node; 681 GtkCMCTreeNode *old_selected = folderview->selected; 682 683 if (!item) return; 684 685 node = gtk_cmctree_find_by_row_data(ctree, NULL, item); 686 if (node) folderview_select_node(folderview, node); 687 688 if (old_selected != node) 689 folder_update_op_count(); 690 } 691 692 static void mark_all_read_cb(GtkAction *action, gpointer data) 693 { 694 mark_all_read_unread_handler(action, data, FALSE, TRUE); 695 } 696 697 static void mark_all_unread_cb(GtkAction *action, gpointer data) 698 { 699 mark_all_read_unread_handler(action, data, FALSE, FALSE); 700 } 701 702 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data) 703 { 704 mark_all_read_unread_handler(action, data, TRUE, TRUE); 705 } 706 707 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data) 708 { 709 mark_all_read_unread_handler(action, data, TRUE, FALSE); 710 } 711 712 static void mark_all_read_unread_handler(GtkAction *action, gpointer data, 713 gboolean recursive, gboolean read) 714 { 715 FolderView *folderview = (FolderView *)data; 716 FolderItem *item; 717 AlertValue val; 718 gchar *message; 719 gchar *title; 720 721 item = folderview_get_selected_item(folderview); 722 if (item == NULL) 723 return; 724 725 if (read) { 726 title = _("Mark all as read"); 727 message = recursive? _("Do you really want to mark all mails in this " 728 "folder and its subfolders as read?") : 729 _("Do you really want to mark all mails in this " 730 "folder as read?"); 731 } else { 732 title = _("Mark all as unread"); 733 message = recursive? _("Do you really want to mark all mails in this " 734 "folder and its subfolders as unread?") : 735 _("Do you really want to mark all mails in this " 736 "folder as unread?"); 737 } 738 if (prefs_common.ask_mark_all_read) { 739 val = alertpanel_full(title, message, 740 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, 741 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION); 742 743 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE) 744 return; 745 else if (val & G_ALERTDISABLE) 746 prefs_common.ask_mark_all_read = FALSE; 747 } 748 749 folder_item_update_freeze(); 750 if (folderview->summaryview->folder_item != item && !recursive) 751 summary_lock(folderview->summaryview); 752 else 753 summary_freeze(folderview->summaryview); 754 755 if (read) { 756 if (recursive) 757 folderutils_mark_all_read_recursive(item, TRUE); 758 else { 759 folderutils_mark_all_read(item, TRUE); 760 } 761 } else { 762 if (recursive) 763 folderutils_mark_all_read_recursive(item, FALSE); 764 else { 765 folderutils_mark_all_read(item, FALSE); 766 } 767 } 768 if (folderview->summaryview->folder_item != item && !recursive) 769 summary_unlock(folderview->summaryview); 770 else 771 summary_thaw(folderview->summaryview); 772 folder_item_update_thaw(); 773 } 774 775 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node) 776 { 777 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 778 779 cm_return_if_fail(node != NULL); 780 781 if (folderview->open_folder) { 782 return; 783 } 784 785 gtk_cmclist_freeze(GTK_CMCLIST(ctree)); 786 gtkut_ctree_expand_parent_all(ctree, node); 787 788 folderview->open_folder = TRUE; 789 gtkut_ctree_set_focus_row(ctree, node); 790 gtk_cmctree_select(ctree, node); 791 gtk_cmclist_thaw(GTK_CMCLIST(ctree)); 792 if ((folderview->summaryview->folder_item && 793 folderview->summaryview->folder_item->total_msgs > 0)) 794 summary_select_node(folderview->summaryview, 795 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN); 796 else 797 gtk_widget_grab_focus(folderview->ctree); 798 } 799 800 void folderview_unselect(FolderView *folderview) 801 { 802 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children) 803 gtk_cmctree_collapse 804 (GTK_CMCTREE(folderview->ctree), folderview->opened); 805 806 folderview->selected = folderview->opened = NULL; 807 } 808 809 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree, 810 GtkCMCTreeNode *node, 811 MsgPermFlags flag) 812 { 813 FolderItem *item; 814 815 if (node) 816 node = gtkut_ctree_node_next(ctree, node); 817 else 818 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 819 820 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 821 item = gtk_cmctree_node_get_row_data(ctree, node); 822 if (!item) 823 continue; 824 if (item->stype == F_TRASH || item->stype == F_DRAFT) 825 continue; 826 switch (flag) { 827 case MSG_UNREAD: 828 if((item->unread_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new)) 829 return node; 830 break; 831 case MSG_NEW: 832 if((item->new_msgs > 0) && (!item->prefs || !item->prefs->skip_on_goto_unread_or_new)) 833 return node; 834 break; 835 case MSG_MARKED: 836 if(item->marked_msgs > 0) 837 return node; 838 break; 839 default: 840 if(item->total_msgs > 0) 841 return node; 842 break; 843 } 844 } 845 846 return NULL; 847 } 848 849 void folderview_select_next_with_flag(FolderView *folderview, 850 MsgPermFlags flag) 851 { 852 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 853 GtkCMCTreeNode *node = NULL; 854 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0]; 855 856 switch (flag) { 857 case MSG_UNREAD: 858 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD; 859 break; 860 case MSG_NEW: 861 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW; 862 break; 863 case MSG_MARKED: 864 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED; 865 break; 866 default: 867 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST; 868 break; 869 } 870 871 node = folderview_find_next_with_flag(ctree, folderview->opened, flag); 872 if (node != NULL) { 873 folderview_select_node(folderview, node); 874 goto out; 875 } 876 877 if (!folderview->opened || 878 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) { 879 goto out; 880 } 881 882 /* search again from the first node */ 883 node = folderview_find_next_with_flag(ctree, NULL, flag); 884 if (node != NULL) 885 folderview_select_node(folderview, node); 886 887 out: 888 prefs_common.summary_select_prio[0] = last_summary_select_prio; 889 } 890 891 FolderItem *folderview_get_selected_item(FolderView *folderview) 892 { 893 g_return_val_if_fail(folderview != NULL, NULL); 894 g_return_val_if_fail(folderview->ctree != NULL, NULL); 895 896 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 897 898 if (!folderview->selected) return NULL; 899 return gtk_cmctree_node_get_row_data(ctree, folderview->selected); 900 } 901 902 FolderItem *folderview_get_opened_item(FolderView *folderview) 903 { 904 g_return_val_if_fail(folderview != NULL, NULL); 905 g_return_val_if_fail(folderview->ctree != NULL, NULL); 906 907 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 908 909 if (!folderview->opened) return NULL; 910 return gtk_cmctree_node_get_row_data(ctree, folderview->opened); 911 } 912 913 static void folderview_set_folders(FolderView *folderview) 914 { 915 GList *list; 916 list = folder_get_list(); 917 918 for (; list != NULL; list = list->next) { 919 folderview_append_folder(folderview, FOLDER(list->data)); 920 } 921 } 922 923 static gchar *get_scan_str(FolderItem *item) 924 { 925 if (item->path) 926 return g_strdup_printf(_("Scanning folder %s/%s..."), 927 item->folder->name, item->path); 928 else 929 return g_strdup_printf(_("Scanning folder %s..."), 930 item->folder->name); 931 } 932 static void folderview_scan_tree_func(Folder *folder, FolderItem *item, 933 gpointer data) 934 { 935 GList *list; 936 for (list = folderview_list; list != NULL; list = list->next) { 937 FolderView *folderview = (FolderView *)list->data; 938 MainWindow *mainwin = folderview->mainwin; 939 gchar *str = get_scan_str(item); 940 941 STATUSBAR_PUSH(mainwin, str); 942 STATUSBAR_POP(mainwin); 943 g_free(str); 944 } 945 } 946 947 void folderview_rescan_tree(Folder *folder, gboolean rebuild) 948 { 949 GtkWidget *window; 950 MainWindow *mainwin = mainwindow_get_mainwindow(); 951 FolderView *folderview = NULL; 952 GtkAdjustment *pos = NULL; 953 gint height = 0; 954 955 cm_return_if_fail(folder != NULL); 956 957 if (!folder->klass->scan_tree) return; 958 959 if (rebuild && 960 alertpanel_full(_("Rebuild folder tree"), 961 _("Rebuilding the folder tree will remove " 962 "local caches. Do you want to continue?"), 963 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, 964 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING) 965 != G_ALERTALTERNATE) { 966 return; 967 } 968 969 inc_lock(); 970 if (rebuild) 971 window = label_window_create(_("Rebuilding folder tree...")); 972 else 973 window = label_window_create(_("Scanning folder tree...")); 974 975 if (mainwin) 976 folderview = mainwin->folderview; 977 978 if (folderview) { 979 pos = gtk_scrolled_window_get_vadjustment( 980 GTK_SCROLLED_WINDOW(folderview->scrolledwin)); 981 height = gtk_adjustment_get_value(pos); 982 } 983 984 folder_set_ui_func(folder, folderview_scan_tree_func, NULL); 985 folder_scan_tree(folder, rebuild); 986 folder_set_ui_func(folder, NULL, NULL); 987 988 folderview_set_all(); 989 990 if (folderview) { 991 pos = gtk_scrolled_window_get_vadjustment( 992 GTK_SCROLLED_WINDOW(folderview->scrolledwin)); 993 gtk_adjustment_set_value(pos, height); 994 } 995 label_window_destroy(window); 996 inc_unlock(); 997 } 998 999 /** folderview_check_new() 1000 * Scan and update the folder and return the 1001 * count the number of new messages since last check. 1002 * \param folder the folder to check for new messages 1003 * \return the number of new messages since last check 1004 */ 1005 gint folderview_check_new(Folder *folder) 1006 { 1007 GList *list; 1008 FolderItem *item; 1009 FolderView *folderview; 1010 GtkCMCTree *ctree; 1011 GtkCMCTreeNode *node; 1012 gint new_msgs = 0; 1013 gint former_new_msgs = 0; 1014 gint former_new = 0, former_unread = 0, former_total; 1015 1016 for (list = folderview_list; list != NULL; list = list->next) { 1017 folderview = (FolderView *)list->data; 1018 ctree = GTK_CMCTREE(folderview->ctree); 1019 folderview->scanning_folder = folder; 1020 inc_lock(); 1021 main_window_lock(folderview->mainwin); 1022 1023 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list); 1024 node != NULL; node = gtkut_ctree_node_next(ctree, node)) { 1025 gchar *str = NULL; 1026 item = gtk_cmctree_node_get_row_data(ctree, node); 1027 if (!item || !item->path || !item->folder) continue; 1028 if (item->no_select) continue; 1029 if (folder && folder != item->folder) continue; 1030 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue; 1031 if (!item->prefs->newmailcheck) continue; 1032 if (item->scanning != ITEM_NOT_SCANNING) { 1033 debug_print("skipping %s, scanning\n", 1034 item->path ? item->path : item->name); 1035 continue; 1036 } 1037 1038 str = get_scan_str(item); 1039 1040 STATUSBAR_PUSH(folderview->mainwin, str); 1041 GTK_EVENTS_FLUSH(); 1042 g_free(str); 1043 1044 folderview_scan_tree_func(item->folder, item, NULL); 1045 former_new = item->new_msgs; 1046 former_unread = item->unread_msgs; 1047 former_total = item->total_msgs; 1048 1049 if (item->folder->klass->scan_required && 1050 (item->folder->klass->scan_required(item->folder, item) || 1051 item->folder->inbox == item || 1052 item->opened == TRUE )) { 1053 if (folder_item_scan(item) < 0) { 1054 if (folder) { 1055 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) { 1056 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"), 1057 item->path ? item->path:item->name); 1058 STATUSBAR_POP(folderview->mainwin); 1059 continue; 1060 } else if (!FOLDER_IS_LOCAL(folder)) { 1061 STATUSBAR_POP(folderview->mainwin); 1062 break; 1063 } 1064 } 1065 } 1066 } else if (!item->folder->klass->scan_required) { 1067 if (folder_item_scan(item) < 0) { 1068 if (folder && !FOLDER_IS_LOCAL(folder)) { 1069 STATUSBAR_POP(folderview->mainwin); 1070 break; 1071 } 1072 } 1073 } 1074 if (former_new != item->new_msgs || 1075 former_unread != item->unread_msgs || 1076 former_total != item->total_msgs) 1077 folderview_update_node(folderview, node); 1078 1079 new_msgs += item->new_msgs; 1080 former_new_msgs += former_new; 1081 STATUSBAR_POP(folderview->mainwin); 1082 } 1083 folderview->scanning_folder = NULL; 1084 main_window_unlock(folderview->mainwin); 1085 inc_unlock(); 1086 } 1087 1088 folder_write_list(); 1089 /* Number of new messages since last check is the just the difference 1090 * between former_new_msgs and new_msgs. If new_msgs is less than 1091 * former_new_msgs, that would mean another session accessed the folder 1092 * and the result is not well defined. 1093 */ 1094 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0); 1095 return new_msgs; 1096 } 1097 1098 void folderview_check_new_all(void) 1099 { 1100 GList *list; 1101 GtkWidget *window; 1102 FolderView *folderview; 1103 1104 folderview = (FolderView *)folderview_list->data; 1105 1106 inc_lock(); 1107 main_window_lock(folderview->mainwin); 1108 window = label_window_create 1109 (_("Checking for new messages in all folders...")); 1110 1111 list = folder_get_list(); 1112 for (; list != NULL; list = list->next) { 1113 Folder *folder = list->data; 1114 1115 folderview_check_new(folder); 1116 } 1117 1118 folder_write_list(); 1119 folderview_set_all(); 1120 1121 label_window_destroy(window); 1122 main_window_unlock(folderview->mainwin); 1123 inc_unlock(); 1124 } 1125 1126 static gboolean folderview_have_children_sub(FolderView *folderview, 1127 FolderItem *item, 1128 gboolean in_sub) 1129 { 1130 GNode *node = NULL; 1131 1132 if (!item || !item->folder || !item->folder->node) 1133 return FALSE; 1134 1135 node = item->folder->node; 1136 1137 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1138 node = node->children; 1139 1140 if (in_sub && item->total_msgs > 0) { 1141 return TRUE; 1142 } 1143 1144 while (node != NULL) { 1145 if (node && node->data) { 1146 FolderItem *next_item = (FolderItem*) node->data; 1147 node = node->next; 1148 if (folderview_have_children_sub(folderview, 1149 next_item, TRUE)) 1150 return TRUE; 1151 } 1152 } 1153 1154 return FALSE; 1155 } 1156 1157 static gboolean folderview_have_children(FolderView *folderview, 1158 FolderItem *item) 1159 { 1160 return folderview_have_children_sub(folderview, item, FALSE); 1161 } 1162 1163 static gboolean folderview_have_new_children_sub(FolderView *folderview, 1164 FolderItem *item, 1165 gboolean in_sub) 1166 { 1167 GNode *node = NULL; 1168 1169 if (!item || !item->folder || !item->folder->node) 1170 return FALSE; 1171 1172 node = item->folder->node; 1173 1174 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1175 node = node->children; 1176 1177 if (in_sub && 1178 (item->new_msgs > 0 || 1179 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) { 1180 return TRUE; 1181 } 1182 1183 while (node != NULL) { 1184 if (node && node->data) { 1185 FolderItem *next_item = (FolderItem*) node->data; 1186 node = node->next; 1187 if (folderview_have_new_children_sub(folderview, 1188 next_item, TRUE)) 1189 return TRUE; 1190 } 1191 } 1192 1193 return FALSE; 1194 } 1195 1196 static gboolean folderview_have_new_children(FolderView *folderview, 1197 FolderItem *item) 1198 { 1199 return folderview_have_new_children_sub(folderview, item, FALSE); 1200 } 1201 1202 static gboolean folderview_have_unread_children_sub(FolderView *folderview, 1203 FolderItem *item, 1204 gboolean in_sub) 1205 { 1206 GNode *node = NULL; 1207 1208 if (!item || !item->folder || !item->folder->node) 1209 return FALSE; 1210 1211 node = item->folder->node; 1212 1213 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1214 node = node->children; 1215 1216 if (in_sub && 1217 (item->unread_msgs > 0 || 1218 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) { 1219 return TRUE; 1220 } 1221 1222 while (node != NULL) { 1223 if (node && node->data) { 1224 FolderItem *next_item = (FolderItem*) node->data; 1225 node = node->next; 1226 if (folderview_have_unread_children_sub(folderview, 1227 next_item, 1228 TRUE)) 1229 return TRUE; 1230 } 1231 } 1232 1233 return FALSE; 1234 } 1235 1236 static gboolean folderview_have_unread_children(FolderView *folderview, 1237 FolderItem *item) 1238 { 1239 return folderview_have_unread_children_sub(folderview, item, FALSE); 1240 } 1241 1242 static gboolean folderview_have_read_children_sub(FolderView *folderview, 1243 FolderItem *item, 1244 gboolean in_sub) 1245 { 1246 GNode *node = NULL; 1247 1248 if (!item || !item->folder || !item->folder->node) { 1249 return FALSE; 1250 } 1251 1252 node = item->folder->node; 1253 1254 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1255 node = node->children; 1256 1257 if (in_sub && 1258 (((item->total_msgs > 0) && 1259 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) { 1260 return TRUE; 1261 } 1262 1263 while (node != NULL) { 1264 if (node && node->data) { 1265 FolderItem *next_item = (FolderItem*) node->data; 1266 node = node->next; 1267 if (folderview_have_read_children_sub(folderview, 1268 next_item, 1269 TRUE)) { 1270 return TRUE; 1271 } 1272 } 1273 } 1274 1275 return FALSE; 1276 } 1277 1278 static gboolean folderview_have_read_children(FolderView *folderview, 1279 FolderItem *item) 1280 { 1281 return folderview_have_read_children_sub(folderview, item, FALSE); 1282 } 1283 1284 static gboolean folderview_have_matching_children_sub(FolderView *folderview, 1285 FolderItem *item, 1286 gboolean in_sub) 1287 { 1288 GNode *node = NULL; 1289 1290 if (!item || !item->folder || !item->folder->node) 1291 return FALSE; 1292 1293 node = item->folder->node; 1294 1295 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1296 node = node->children; 1297 1298 if (in_sub && item->search_match){ 1299 return TRUE; 1300 } 1301 1302 while (node != NULL) { 1303 if (node && node->data) { 1304 FolderItem *next_item = (FolderItem*) node->data; 1305 node = node->next; 1306 if (folderview_have_matching_children_sub(folderview, 1307 next_item, 1308 TRUE)) 1309 return TRUE; 1310 } 1311 } 1312 1313 return FALSE; 1314 } 1315 1316 static gboolean folderview_have_matching_children(FolderView *folderview, 1317 FolderItem *item) 1318 { 1319 return folderview_have_matching_children_sub(folderview, item, FALSE); 1320 } 1321 1322 static gboolean folderview_have_marked_children_sub(FolderView *folderview, 1323 FolderItem *item, 1324 gboolean in_sub) 1325 { 1326 GNode *node = NULL; 1327 1328 if (!item || !item->folder || !item->folder->node) 1329 return FALSE; 1330 1331 node = item->folder->node; 1332 1333 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 1334 node = node->children; 1335 1336 if (item->marked_msgs != 0) { 1337 return TRUE; 1338 } 1339 1340 while (node != NULL) { 1341 if (node && node->data) { 1342 FolderItem *next_item = (FolderItem*) node->data; 1343 node = node->next; 1344 if (folderview_have_marked_children_sub(folderview, 1345 next_item, TRUE)) 1346 return TRUE; 1347 } 1348 } 1349 1350 return FALSE; 1351 } 1352 1353 static gboolean folderview_have_marked_children(FolderView *folderview, 1354 FolderItem *item) 1355 { 1356 return folderview_have_marked_children_sub(folderview, item, FALSE); 1357 } 1358 1359 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node) 1360 { 1361 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 1362 GtkStyle *style = NULL, *prev_style; 1363 FolderItem *item; 1364 GdkPixbuf *xpm, *openxpm; 1365 static GdkPixbuf *searchicon; 1366 gboolean mark = FALSE; 1367 gchar *name; 1368 gchar *str; 1369 gboolean add_unread_mark; 1370 gboolean add_sub_match_mark; 1371 gboolean use_bold, use_color; 1372 gint *col_pos = folderview->col_pos; 1373 SpecialFolderItemType stype; 1374 1375 item = gtk_cmctree_node_get_row_data(ctree, node); 1376 cm_return_if_fail(item != NULL); 1377 1378 if (!GTK_CMCTREE_ROW(node)->expanded) 1379 mark = folderview_have_marked_children(folderview, item); 1380 else 1381 mark = (item->marked_msgs != 0); 1382 1383 stype = item->stype; 1384 if (stype == F_NORMAL) { 1385 if (folder_has_parent_of_type(item, F_TRASH)) 1386 stype = F_TRASH; 1387 else if (folder_has_parent_of_type(item, F_DRAFT)) 1388 stype = F_DRAFT; 1389 else if (folder_has_parent_of_type(item, F_OUTBOX)) 1390 stype = F_OUTBOX; 1391 else if (folder_has_parent_of_type(item, F_QUEUE)) 1392 stype = F_QUEUE; 1393 } 1394 switch (stype) { 1395 case F_INBOX: 1396 if (item->hide_read_msgs || item->hide_read_threads) { 1397 xpm = mark?m_inboxhrmxpm:inboxhrmxpm; 1398 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm; 1399 } else { 1400 xpm = mark?m_inboxxpm:inboxxpm; 1401 openxpm = mark?m_inboxopenxpm:inboxopenxpm; 1402 } 1403 break; 1404 case F_OUTBOX: 1405 if (item->hide_read_msgs || item->hide_read_threads) { 1406 xpm = mark?m_outboxhrmxpm:outboxhrmxpm; 1407 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm; 1408 } else { 1409 xpm = mark?m_outboxxpm:outboxxpm; 1410 openxpm = mark?m_outboxopenxpm:outboxopenxpm; 1411 } 1412 break; 1413 case F_QUEUE: 1414 if (item->hide_read_msgs || item->hide_read_threads) { 1415 xpm = mark?m_queuehrmxpm:queuehrmxpm; 1416 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm; 1417 } else { 1418 xpm = mark?m_queuexpm:queuexpm; 1419 openxpm = mark?m_queueopenxpm:queueopenxpm; 1420 } 1421 break; 1422 case F_TRASH: 1423 if (item->hide_read_msgs || item->hide_read_threads) { 1424 xpm = mark?m_trashhrmxpm:trashhrmxpm; 1425 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm; 1426 } else { 1427 xpm = mark?m_trashxpm:trashxpm; 1428 openxpm = mark?m_trashopenxpm:trashopenxpm; 1429 } 1430 break; 1431 case F_DRAFT: 1432 xpm = mark?m_draftsxpm:draftsxpm; 1433 openxpm = mark?m_draftsopenxpm:draftsopenxpm; 1434 break; 1435 default: 1436 if (!item->path && 1437 FOLDER_TYPE(item->folder) == F_IMAP && 1438 item->folder->account->imap_subsonly) { 1439 xpm = mark?m_foldersubsxpm:foldersubsxpm; 1440 openxpm = foldersubsopenxpm; 1441 } else if (item->no_select) { 1442 xpm = mark?m_foldernoselectxpm:foldernoselectxpm; 1443 openxpm = foldernoselectopenxpm; 1444 } else if (item->hide_read_msgs || item->hide_read_threads) { 1445 xpm = mark?m_folderhrmxpm:folderhrmxpm; 1446 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm; 1447 } else { 1448 xpm = mark?m_folderxpm:folderxpm; 1449 openxpm = mark?m_folderopenxpm:folderopenxpm; 1450 } 1451 } 1452 1453 name = folder_item_get_name(item); 1454 1455 if (!GTK_CMCTREE_ROW(node)->expanded) { 1456 add_unread_mark = folderview_have_unread_children( 1457 folderview, item); 1458 add_sub_match_mark = folderview_have_matching_children( 1459 folderview, item); 1460 } else { 1461 add_unread_mark = FALSE; 1462 add_sub_match_mark = FALSE; 1463 } 1464 1465 if (item->search_match) { 1466 if (!searchicon) { 1467 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH, 1468 &searchicon); 1469 } 1470 xpm = openxpm = searchicon; 1471 } 1472 1473 str = NULL; 1474 if (prefs_common.display_folder_unread) { 1475 if (folder_has_parent_of_type(item, F_QUEUE)) { 1476 /* only total_msgs matters here */ 1477 if (item->total_msgs > 0) { 1478 /* show total number (should be equal to the unread number) 1479 and signs if any */ 1480 str = g_strdup_printf("%s (%d%s%s)", 1481 name, item->total_msgs, 1482 (add_unread_mark || add_sub_match_mark) ? "+" : "", 1483 (item->unreadmarked_msgs > 0) ? "!" : ""); 1484 } 1485 } else { 1486 if (prefs_common.display_folder_unread == 1) { 1487 if (item->unread_msgs > 0) { 1488 /* show unread number and signs */ 1489 str = g_strdup_printf("%s (%d%s%s)", 1490 name, item->unread_msgs, 1491 (add_unread_mark || add_sub_match_mark) ? "+" : "", 1492 (item->unreadmarked_msgs > 0) ? "!" : ""); 1493 } 1494 } else { 1495 if (item->total_msgs > 0) { 1496 /* show unread number, total number and signs if any */ 1497 str = g_strdup_printf("%s (%d/%d%s%s)", 1498 name, item->unread_msgs, item->total_msgs, 1499 (add_unread_mark || add_sub_match_mark) ? "+" : "", 1500 (item->unreadmarked_msgs > 0) ? "!" : ""); 1501 } 1502 } 1503 } 1504 if ((str == NULL) && 1505 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) { 1506 /* no unread/total numbers, but at least one sign */ 1507 str = g_strdup_printf("%s (%s%s)", 1508 name, 1509 (add_unread_mark || add_sub_match_mark) ? "+" : "", 1510 (item->unreadmarked_msgs > 0) ? "!" : ""); 1511 } 1512 } 1513 if (str == NULL) { 1514 /* last fallback, folder name only or with +! sign */ 1515 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) { 1516 str = g_strdup_printf("%s%s", 1517 name, " (+!)"); 1518 } else if (item->unreadmarked_msgs > 0) { 1519 str = g_strdup_printf("%s%s", 1520 name, " (!)"); 1521 } else if (add_sub_match_mark) { 1522 str = g_strdup_printf("%s%s", 1523 name, " (+)"); 1524 } else { 1525 str = g_strdup_printf("%s", name); 1526 } 1527 } 1528 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING, 1529 xpm, openxpm, 1530 FALSE, GTK_CMCTREE_ROW(node)->expanded); 1531 g_free(str); 1532 g_free(name); 1533 1534 if (!folder_item_parent(item)) { 1535 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-"); 1536 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-"); 1537 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-"); 1538 } else { 1539 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement); 1540 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement); 1541 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement); 1542 } 1543 1544 if (folder_has_parent_of_type(item, F_OUTBOX) || 1545 folder_has_parent_of_type(item, F_TRASH)) { 1546 use_bold = use_color = FALSE; 1547 } else if (folder_has_parent_of_type(item, F_QUEUE)) { 1548 GSList *list = folder_item_get_msg_list(item); 1549 GSList *cur; 1550 use_bold = use_color = FALSE; 1551 for (cur = list; cur; cur = cur->next) { 1552 MsgInfo *msginfo = (MsgInfo *)cur->data; 1553 if (!MSG_IS_DELETED(msginfo->flags)) { 1554 /* highlight queue folder if there are any messages */ 1555 use_bold = use_color = TRUE; 1556 break; 1557 } 1558 } 1559 if (!GTK_CMCTREE_ROW(node)->expanded && 1560 use_bold == FALSE && 1561 folderview_have_children(folderview, item)) 1562 use_bold = use_color = TRUE; 1563 procmsg_msg_list_free(list); 1564 } else { 1565 /* if unread messages exist or target folder is set, print with bold font */ 1566 use_bold = (item->unread_msgs > 0 || item->new_msgs > 0 || item->op_count > 0) 1567 || add_unread_mark; 1568 /* if new messages exist, print with colored letter */ 1569 use_color = 1570 (item->new_msgs > 0) || 1571 (add_unread_mark && 1572 folderview_have_new_children(folderview, item)); 1573 } 1574 1575 gtk_cmctree_node_set_foreground(ctree, node, NULL); 1576 1577 if (use_bold) { 1578 style = bold_style; 1579 if (item->op_count > 0) 1580 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op); 1581 else if (use_color) 1582 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new); 1583 } else if (use_color) 1584 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new); 1585 else if (item->op_count > 0) 1586 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op); 1587 1588 gtk_cmctree_node_set_row_style(ctree, node, style); 1589 1590 prev_style = gtk_cmctree_node_get_row_style(ctree, node); 1591 if (prev_style) { 1592 GtkStyle *ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree)); 1593 1594 style = gtk_style_copy(prev_style); 1595 style->text[GTK_STATE_NORMAL] = ctree_style->text[GTK_STATE_NORMAL]; 1596 style->text[GTK_STATE_SELECTED] = ctree_style->text[GTK_STATE_SELECTED]; 1597 gtk_cmctree_node_set_row_style(ctree, node, style); 1598 g_object_unref(style); 1599 } 1600 1601 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL) 1602 folderview_update_node(folderview, node); 1603 } 1604 1605 static gboolean folderview_update_item_claws(gpointer source, gpointer data) 1606 { 1607 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source; 1608 FolderView *folderview = (FolderView *)data; 1609 GtkCMCTree *ctree; 1610 GtkCMCTreeNode *node; 1611 cm_return_val_if_fail(update_info != NULL, TRUE); 1612 cm_return_val_if_fail(update_info->item != NULL, TRUE); 1613 cm_return_val_if_fail(folderview != NULL, FALSE); 1614 1615 ctree = GTK_CMCTREE(folderview->ctree); 1616 1617 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item); 1618 1619 if (node) { 1620 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME)) 1621 folderview_update_node(folderview, node); 1622 1623 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) && 1624 update_info->item == folderview->summaryview->folder_item && 1625 update_info->item != NULL) 1626 summary_show(folderview->summaryview, update_info->item, FALSE); 1627 } 1628 1629 return FALSE; 1630 } 1631 1632 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth, 1633 GNode *gnode, GtkCMCTreeNode *cnode, 1634 gpointer data) 1635 { 1636 FolderView *folderview = (FolderView *)data; 1637 FolderItem *item = FOLDER_ITEM(gnode->data); 1638 1639 cm_return_val_if_fail(item != NULL, FALSE); 1640 1641 gtk_cmctree_node_set_row_data(ctree, cnode, item); 1642 folderview_update_node(folderview, cnode); 1643 1644 return TRUE; 1645 } 1646 1647 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node, 1648 gpointer data) 1649 { 1650 FolderView *folderview = (FolderView *)data; 1651 FolderItem *item; 1652 1653 if (GTK_CMCTREE_ROW(node)->children) { 1654 item = gtk_cmctree_node_get_row_data(ctree, node); 1655 cm_return_if_fail(item != NULL); 1656 1657 if (!item->collapsed) 1658 gtk_cmctree_expand(ctree, node); 1659 else 1660 folderview_update_node(folderview, node); 1661 } 1662 } 1663 1664 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item, 1665 GtkCMCTreeNode *root, GtkCMCTreeNode **prev) 1666 { 1667 if (item) { 1668 GtkCMCTreeNode *node, *parent, *sibling; 1669 1670 node = gtk_cmctree_find_by_row_data(ctree, root, item); 1671 if (!node) 1672 g_warning("%s not found", item->path); 1673 else { 1674 parent = GTK_CMCTREE_ROW(node)->parent; 1675 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent) 1676 sibling = GTK_CMCTREE_ROW(*prev)->sibling; 1677 else 1678 sibling = GTK_CMCTREE_ROW(parent)->children; 1679 while (sibling) { 1680 FolderItem *tmp; 1681 1682 tmp = gtk_cmctree_node_get_row_data 1683 (ctree, sibling); 1684 if (tmp && tmp->stype != F_NORMAL) 1685 sibling = GTK_CMCTREE_ROW(sibling)->sibling; 1686 else 1687 break; 1688 } 1689 if (node != sibling) 1690 gtk_cmctree_move(ctree, node, parent, sibling); 1691 } 1692 1693 *prev = node; 1694 } 1695 } 1696 1697 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root, 1698 Folder *folder) 1699 { 1700 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 1701 GtkCMCTreeNode *prev = NULL; 1702 1703 gtk_cmclist_freeze(GTK_CMCLIST(ctree)); 1704 gtk_sctree_sort_recursive(ctree, root); 1705 if (root && GTK_CMCTREE_ROW(root)->parent) { 1706 gtk_cmclist_thaw(GTK_CMCLIST(ctree)); 1707 return; 1708 } 1709 set_special_folder(ctree, folder->inbox, root, &prev); 1710 set_special_folder(ctree, folder->outbox, root, &prev); 1711 set_special_folder(ctree, folder->draft, root, &prev); 1712 set_special_folder(ctree, folder->queue, root, &prev); 1713 set_special_folder(ctree, folder->trash, root, &prev); 1714 gtk_cmclist_thaw(GTK_CMCLIST(ctree)); 1715 } 1716 1717 static void folderview_append_folder(FolderView *folderview, Folder *folder) 1718 { 1719 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 1720 GtkCMCTreeNode *root; 1721 1722 cm_return_if_fail(folder != NULL); 1723 1724 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node, 1725 folderview_gnode_func, folderview); 1726 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func, 1727 folderview); 1728 folderview_sort_folders(folderview, root, folder); 1729 } 1730 1731 /* callback functions */ 1732 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row, 1733 GdkEventButton *event) 1734 { 1735 FolderItem *item; 1736 Folder *folder; 1737 FolderViewPopup *fpopup; 1738 GtkActionGroup *action_group; 1739 GtkWidget *popup; 1740 FolderItem *special_trash = NULL, *special_queue = NULL; 1741 PrefsAccount *ac; 1742 GtkUIManager *ui_manager = gtk_ui_manager_new(); 1743 1744 if (folderview->ui_manager) 1745 g_object_unref(folderview->ui_manager); 1746 1747 folderview->ui_manager = ui_manager; 1748 item = folderview_get_selected_item(folderview); 1749 1750 cm_return_if_fail(item != NULL); 1751 cm_return_if_fail(item->folder != NULL); 1752 folder = item->folder; 1753 1754 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr); 1755 1756 if (fpopup != NULL) 1757 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr); 1758 else { 1759 fpopup = g_hash_table_lookup(folderview_popups, "common"); 1760 action_group = g_hash_table_lookup(folderview->popups, "common"); 1761 } 1762 1763 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); 1764 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR) 1765 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU) 1766 1767 if (fpopup->add_menuitems) 1768 fpopup->add_menuitems(ui_manager, item); 1769 1770 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM) 1771 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM) 1772 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM) 1773 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM) 1774 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR) 1775 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "OpenFolder", "FolderViewPopup/OpenFolder", GTK_UI_MANAGER_MENUITEM) 1776 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM) 1777 1778 if (fpopup->set_sensitivity != NULL) 1779 fpopup->set_sensitivity(ui_manager, item); 1780 1781 if (NULL != (ac = account_find_from_item(item))) { 1782 special_trash = account_get_special_folder(ac, F_TRASH); 1783 special_queue = account_get_special_folder(ac, F_QUEUE); 1784 } 1785 1786 if ((item == folder->trash || item == special_trash 1787 || folder_has_parent_of_type(item, F_TRASH))) { 1788 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR) 1789 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM) 1790 } 1791 1792 if ((item == folder->queue || item == special_queue 1793 || folder_has_parent_of_type(item, F_QUEUE))) { 1794 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR) 1795 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM) 1796 } 1797 1798 #define SET_SENS(name, sens) \ 1799 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens) 1800 1801 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0); 1802 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) && 1803 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))); 1804 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item)); 1805 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item)); 1806 SET_SENS("FolderViewPopup/Properties", TRUE); 1807 1808 if (item->node->parent != NULL) { 1809 gchar *id = folder_item_get_identifier(item); 1810 SET_SENS("FolderViewPopup/OpenFolder", !prefs_common.goto_folder_on_startup 1811 || strcmp(id, prefs_common.startup_folder)); 1812 g_free(id); 1813 } 1814 1815 if (item == folder->trash || item == special_trash 1816 || folder_has_parent_of_type(item, F_TRASH)) { 1817 GSList *msglist = folder_item_get_msg_list(item); 1818 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL); 1819 procmsg_msg_list_free(msglist); 1820 } 1821 if (item == folder->queue || item == special_queue 1822 || folder_has_parent_of_type(item, F_QUEUE)) { 1823 GSList *msglist = folder_item_get_msg_list(item); 1824 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL); 1825 procmsg_msg_list_free(msglist); 1826 } 1827 #undef SET_SENS 1828 1829 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM( 1830 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) ); 1831 g_signal_connect(G_OBJECT(popup), "selection_done", 1832 G_CALLBACK(folderview_popup_close), 1833 folderview); 1834 gtk_menu_popup_at_pointer(GTK_MENU(popup), NULL); 1835 } 1836 1837 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event, 1838 FolderView *folderview) 1839 { 1840 GtkCMCList *clist = GTK_CMCLIST(ctree); 1841 gint prev_row = -1, row = -1, column = -1; 1842 1843 if (!event) return FALSE; 1844 if (event->window != clist->clist_window) return FALSE; 1845 1846 if (event->button == 1 || event->button == 2) { 1847 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y)) 1848 folderview->open_folder = TRUE; 1849 1850 if (event->type == GDK_2BUTTON_PRESS) { 1851 if (clist->selection) { 1852 GtkCMCTreeNode *node; 1853 1854 node = GTK_CMCTREE_NODE(clist->selection->data); 1855 if (node) { 1856 gtk_cmctree_toggle_expansion( 1857 GTK_CMCTREE(ctree), 1858 node); 1859 folderview->open_folder = FALSE; 1860 } 1861 } 1862 } 1863 return FALSE; 1864 } 1865 1866 if (event->button == 2 || event->button == 3) { 1867 /* right clicked */ 1868 if (clist->selection) { 1869 GtkCMCTreeNode *node; 1870 1871 node = GTK_CMCTREE_NODE(clist->selection->data); 1872 if (node) 1873 prev_row = gtkut_ctree_get_nth_from_node 1874 (GTK_CMCTREE(ctree), node); 1875 } 1876 1877 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y, 1878 &row, &column)) 1879 return FALSE; 1880 if (prev_row != row) { 1881 gtk_cmclist_unselect_all(clist); 1882 if (event->button == 2) 1883 folderview_select_node 1884 (folderview, 1885 gtk_cmctree_node_nth(GTK_CMCTREE(ctree), 1886 row)); 1887 else 1888 gtk_cmclist_select_row(clist, row, column); 1889 } 1890 } 1891 1892 if (event->button != 3) return FALSE; 1893 1894 folderview_set_sens_and_popup_menu(folderview, row, event); 1895 return FALSE; 1896 } 1897 1898 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event, 1899 FolderView *folderview) 1900 { 1901 int row = -1, column = -1; 1902 1903 if (!event) return FALSE; 1904 1905 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y, 1906 &row, &column)) 1907 return FALSE; 1908 if (event->button == 1 && folderview->open_folder == FALSE && 1909 folderview->opened != NULL) { 1910 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree), 1911 folderview->opened); 1912 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened); 1913 } 1914 1915 return FALSE; 1916 } 1917 1918 #define BREAK_ON_MODIFIER_KEY() \ 1919 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break 1920 1921 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event, 1922 FolderView *folderview) 1923 { 1924 GtkCMCTreeNode *node; 1925 FolderItem *item; 1926 1927 if (!event) return FALSE; 1928 1929 switch (event->keyval) { 1930 case GDK_KEY_Right: 1931 if (folderview->selected) { 1932 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL 1933 && !GTK_CMCTREE_ROW(folderview->selected)->expanded) 1934 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), 1935 folderview->selected); 1936 else 1937 folderview_select_node(folderview, 1938 folderview->selected); 1939 } 1940 break; 1941 case GDK_KEY_Return: 1942 case GDK_KEY_KP_Enter: 1943 if (folderview->selected) 1944 folderview_select_node(folderview, folderview->selected); 1945 break; 1946 case GDK_KEY_space: 1947 BREAK_ON_MODIFIER_KEY(); 1948 if (folderview->selected) { 1949 if (folderview->opened == folderview->selected && 1950 (!folderview->summaryview->folder_item || 1951 folderview->summaryview->folder_item->total_msgs == 0)) 1952 folderview_select_next_with_flag(folderview, MSG_UNREAD); 1953 else 1954 folderview_select_node(folderview, 1955 folderview->selected); 1956 } 1957 break; 1958 case GDK_KEY_Left: 1959 if (folderview->selected) { 1960 /* If the folder is expanded and can be collapsed, do that... */ 1961 if (GTK_CMCTREE_ROW(folderview->selected)->expanded && 1962 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) { 1963 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), 1964 folderview->selected); 1965 } else { 1966 /* ...otherwise, move cursor to its parent node. */ 1967 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree), 1968 folderview->selected))) { 1969 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), 1970 NULL, folder_item_parent(item)))) { 1971 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node); 1972 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node)) 1973 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree), 1974 node, -1, 0, 0); 1975 } 1976 } 1977 } 1978 } 1979 break; 1980 case GDK_KEY_Home: 1981 case GDK_KEY_End: 1982 if (event->keyval == GDK_KEY_Home) 1983 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0); 1984 else 1985 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree), 1986 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0)); 1987 1988 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node); 1989 1990 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node)) 1991 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree), 1992 node, -1, 0, 0); 1993 break; 1994 default: 1995 break; 1996 } 1997 1998 return FALSE; 1999 } 2000 2001 typedef struct _PostponedSelectData 2002 { 2003 GtkCMCTree *ctree; 2004 GtkCMCTreeNode *row; 2005 gint column; 2006 FolderView *folderview; 2007 } PostponedSelectData; 2008 2009 static gboolean postpone_select(void *data) 2010 { 2011 PostponedSelectData *psdata = (PostponedSelectData *)data; 2012 debug_print("trying again\n"); 2013 2014 psdata->folderview->postpone_select_id = 0; 2015 psdata->folderview->open_folder = TRUE; 2016 main_window_cursor_normal(psdata->folderview->mainwin); 2017 STATUSBAR_POP(psdata->folderview->mainwin); 2018 folderview_selected(psdata->ctree, psdata->row, 2019 psdata->column, psdata->folderview); 2020 g_free(psdata); 2021 return FALSE; 2022 } 2023 2024 void folderview_close_opened(FolderView *folderview, gboolean dirty) 2025 { 2026 if (folderview->opened) { 2027 if (dirty) { 2028 folderview->opened = NULL; 2029 return; 2030 } 2031 2032 FolderItem *olditem = 2033 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree), 2034 folderview->opened); 2035 if (olditem) { 2036 gchar *buf = g_strdup_printf(_("Closing folder %s..."), 2037 olditem->path ? olditem->path:olditem->name); 2038 /* will be null if we just moved the previously opened folder */ 2039 STATUSBAR_PUSH(folderview->mainwin, buf); 2040 main_window_cursor_wait(folderview->mainwin); 2041 g_free(buf); 2042 summary_save_prefs_to_folderitem(folderview->summaryview, olditem); 2043 summary_show(folderview->summaryview, NULL, FALSE); 2044 folder_item_close(olditem); 2045 main_window_cursor_normal(folderview->mainwin); 2046 STATUSBAR_POP(folderview->mainwin); 2047 if (olditem->folder->klass->item_closed) 2048 olditem->folder->klass->item_closed(olditem); 2049 2050 } 2051 } 2052 2053 if (folderview->opened && 2054 !GTK_CMCTREE_ROW(folderview->opened)->children) 2055 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened); 2056 2057 folderview->opened = NULL; 2058 } 2059 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row, 2060 gint column, FolderView *folderview) 2061 { 2062 GdkDisplay *display; 2063 GdkSeat *seat; 2064 GdkDevice *device; 2065 static gboolean can_select = TRUE; /* exclusive lock */ 2066 gboolean opened; 2067 FolderItem *item; 2068 int res = 0; 2069 GtkCMCTreeNode *old_opened = folderview->opened; 2070 folderview->selected = row; 2071 2072 display = gdk_display_get_default(); 2073 seat = gdk_display_get_default_seat(display); 2074 device = gdk_seat_get_pointer(seat); 2075 2076 debug_print("newly selected %p, opened %p\n", folderview->selected, 2077 folderview->opened); 2078 if (folderview->opened == row) { 2079 folderview->open_folder = FALSE; 2080 return; 2081 } 2082 2083 item = gtk_cmctree_node_get_row_data(ctree, row); 2084 if (!item) { 2085 folderview->open_folder = FALSE; 2086 return; 2087 } 2088 2089 if (!can_select || summary_is_locked(folderview->summaryview)) { 2090 if (folderview->opened) { 2091 gtkut_ctree_set_focus_row(ctree, folderview->opened); 2092 gtk_cmctree_select(ctree, folderview->opened); 2093 } 2094 folderview->open_folder = FALSE; 2095 return; 2096 } 2097 2098 if (!folderview->open_folder) { 2099 return; 2100 } 2101 2102 can_select = FALSE; 2103 2104 /* Save cache for old folder */ 2105 /* We don't want to lose all caches if app crashes */ 2106 /* Resets folderview->opened to NULL */ 2107 folderview_close_opened(folderview, FALSE); 2108 2109 /* CLAWS: set compose button type: news folder items 2110 * always have a news folder as parent */ 2111 if (item->folder) 2112 toolbar_set_compose_button 2113 (folderview->mainwin->toolbar, 2114 FOLDER_TYPE(item->folder) == F_NEWS ? 2115 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL); 2116 2117 if (item->path) 2118 debug_print("Folder %s is selected\n", item->path); 2119 2120 if (!GTK_CMCTREE_ROW(row)->children) 2121 gtk_cmctree_expand(ctree, row); 2122 2123 /* ungrab the mouse event */ 2124 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) { 2125 gtk_grab_remove(GTK_WIDGET(ctree)); 2126 if (gdk_display_device_is_grabbed(display, device)) 2127 gdk_seat_ungrab(seat); 2128 } 2129 2130 if (item->path) { 2131 gchar *buf; 2132 buf = g_strdup_printf(_("Opening folder %s..."), item->path); 2133 debug_print("%s\n", buf); 2134 STATUSBAR_PUSH(folderview->mainwin, buf); 2135 g_free(buf); 2136 } 2137 2138 main_window_cursor_wait(folderview->mainwin); 2139 2140 if (folderview->scanning_folder == item->folder) { 2141 res = -2; 2142 } else { 2143 res = folder_item_open(item); 2144 } 2145 2146 if (res == -1 && item->no_select == FALSE) { 2147 main_window_cursor_normal(folderview->mainwin); 2148 STATUSBAR_POP(folderview->mainwin); 2149 2150 alertpanel_error(_("Folder could not be opened.")); 2151 2152 folderview->open_folder = FALSE; 2153 can_select = TRUE; 2154 return; 2155 } else if (res == -2 && item->no_select == FALSE) { 2156 PostponedSelectData *data = g_new0(PostponedSelectData, 1); 2157 data->ctree = ctree; 2158 data->row = row; 2159 data->column = column; 2160 data->folderview = folderview; 2161 debug_print("postponing open of %s till end of scan\n", 2162 item->path ? item->path:item->name); 2163 folderview->open_folder = FALSE; 2164 can_select = TRUE; 2165 if (folderview->postpone_select_id != 0) 2166 g_source_remove(folderview->postpone_select_id); 2167 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data); 2168 return; 2169 } 2170 2171 main_window_cursor_normal(folderview->mainwin); 2172 2173 /* Show messages */ 2174 summary_set_prefs_from_folderitem(folderview->summaryview, item); 2175 opened = summary_show(folderview->summaryview, item, FALSE); 2176 2177 folder_clean_cache_memory(item); 2178 2179 if (!opened) { 2180 gtkut_ctree_set_focus_row(ctree, old_opened); 2181 gtk_cmctree_select(ctree, old_opened); 2182 folderview->opened = old_opened; 2183 } else { 2184 folderview->opened = row; 2185 if (gtk_cmctree_node_is_visible(ctree, row) 2186 != GTK_VISIBILITY_FULL) 2187 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0); 2188 } 2189 2190 STATUSBAR_POP(folderview->mainwin); 2191 2192 folderview->open_folder = FALSE; 2193 can_select = TRUE; 2194 } 2195 2196 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node, 2197 FolderView *folderview) 2198 { 2199 FolderItem *item; 2200 2201 item = gtk_cmctree_node_get_row_data(ctree, node); 2202 cm_return_if_fail(item != NULL); 2203 item->collapsed = FALSE; 2204 folderview_update_node(folderview, node); 2205 } 2206 2207 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node, 2208 FolderView *folderview) 2209 { 2210 FolderItem *item; 2211 2212 item = gtk_cmctree_node_get_row_data(ctree, node); 2213 cm_return_if_fail(item != NULL); 2214 item->collapsed = TRUE; 2215 folderview_update_node(folderview, node); 2216 } 2217 2218 static void folderview_popup_close(GtkMenuShell *menu_shell, 2219 FolderView *folderview) 2220 { 2221 if (!folderview->opened) return; 2222 2223 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened); 2224 } 2225 2226 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width, 2227 FolderView *folderview) 2228 { 2229 FolderColumnType type = folderview->col_state[column].type; 2230 2231 prefs_common.folder_col_size[type] = width; 2232 } 2233 2234 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item) 2235 { 2236 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 2237 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"}; 2238 GtkCMCTreeNode *node, *parent_node; 2239 gint *col_pos = folderview->col_pos; 2240 FolderItemUpdateData hookdata; 2241 2242 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item)); 2243 if (parent_node == NULL) 2244 return; 2245 2246 gtk_cmclist_freeze(GTK_CMCLIST(ctree)); 2247 2248 text[col_pos[F_COL_FOLDER]] = item->name; 2249 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text, 2250 FOLDER_SPACING, 2251 folderxpm, 2252 folderopenxpm, 2253 FALSE, FALSE); 2254 gtk_cmctree_expand(ctree, parent_node); 2255 gtk_cmctree_node_set_row_data(ctree, node, item); 2256 folderview_sort_folders(folderview, parent_node, item->folder); 2257 2258 hookdata.item = item; 2259 hookdata.update_flags = F_ITEM_UPDATE_NAME; 2260 hookdata.msg = NULL; 2261 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata); 2262 2263 gtk_cmclist_thaw(GTK_CMCLIST(ctree)); 2264 } 2265 2266 static void folderview_empty_trash_cb(GtkAction *action, gpointer data) 2267 { 2268 FolderView *folderview = (FolderView *)data; 2269 FolderItem *item; 2270 GSList *mlist = NULL; 2271 GSList *cur = NULL; 2272 FolderItem *special_trash = NULL; 2273 PrefsAccount *ac; 2274 2275 if (!folderview->selected) return; 2276 item = folderview_get_selected_item(folderview); 2277 cm_return_if_fail(item != NULL); 2278 cm_return_if_fail(item->folder != NULL); 2279 2280 if (NULL != (ac = account_find_from_item(item))) 2281 special_trash = account_get_special_folder(ac, F_TRASH); 2282 2283 if (item != item->folder->trash && item != special_trash 2284 && !folder_has_parent_of_type(item, F_TRASH)) return; 2285 2286 if (prefs_common.ask_on_clean) { 2287 if (alertpanel(_("Empty trash"), 2288 _("Delete all messages in trash?"), 2289 NULL, _("_Cancel"), NULL, _("_Empty trash"), NULL, NULL, 2290 ALERTFOCUS_SECOND) != G_ALERTALTERNATE) 2291 return; 2292 } 2293 2294 mlist = folder_item_get_msg_list(item); 2295 2296 for (cur = mlist ; cur != NULL ; cur = cur->next) { 2297 MsgInfo * msginfo = (MsgInfo *) cur->data; 2298 if (MSG_IS_LOCKED(msginfo->flags)) 2299 continue; 2300 } 2301 procmsg_msg_list_free(mlist); 2302 2303 folder_item_remove_all_msg(item); 2304 } 2305 2306 static void folderview_send_queue_cb(GtkAction *action, gpointer data) 2307 { 2308 FolderView *folderview = (FolderView *)data; 2309 FolderItem *item; 2310 FolderItem *special_queue = NULL; 2311 PrefsAccount *ac; 2312 gchar *errstr = NULL; 2313 2314 if (!folderview->selected) return; 2315 item = folderview_get_selected_item(folderview); 2316 cm_return_if_fail(item != NULL); 2317 cm_return_if_fail(item->folder != NULL); 2318 2319 if (NULL != (ac = account_find_from_item(item))) 2320 special_queue = account_get_special_folder(ac, F_QUEUE); 2321 2322 if (item != item->folder->queue && item != special_queue 2323 && !folder_has_parent_of_type(item, F_QUEUE)) return; 2324 2325 if (procmsg_queue_is_empty(item)) 2326 return; 2327 2328 if (prefs_common.work_offline) 2329 if (alertpanel(_("Offline warning"), 2330 _("You're working offline. Override?"), 2331 NULL, _("_No"), NULL, _("_Yes"), 2332 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE) 2333 return; 2334 2335 /* ask for confirmation before sending queued messages only 2336 in online mode and if there is at least one message queued 2337 in any of the folder queue 2338 */ 2339 if (prefs_common.confirm_send_queued_messages) { 2340 if (!prefs_common.work_offline) { 2341 if (alertpanel(_("Send queued messages"), 2342 _("Send all queued messages?"), 2343 NULL, _("_Cancel"), NULL, _("_Send"), 2344 NULL, NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE) 2345 return; 2346 } 2347 } 2348 2349 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) { 2350 if (!errstr) 2351 alertpanel_error_log(_("Some errors occurred while " 2352 "sending queued messages.")); 2353 else { 2354 alertpanel_error_log(_("Some errors occurred " 2355 "while sending queued messages:\n%s"), errstr); 2356 g_free(errstr); 2357 } 2358 } 2359 } 2360 2361 static void folderview_startup_folder_cb(GtkAction *action, gpointer data) 2362 { 2363 FolderView *folderview = (FolderView *)data; 2364 FolderItem *item; 2365 2366 if (!folderview->selected) return; 2367 2368 item = folderview_get_selected_item(folderview); 2369 2370 prefs_common.startup_folder = folder_item_get_identifier(item); 2371 prefs_common.goto_folder_on_startup = prefs_common.startup_folder? TRUE : FALSE; 2372 } 2373 2374 static void folderview_property_cb(GtkAction *action, gpointer data) 2375 { 2376 FolderView *folderview = (FolderView *)data; 2377 FolderItem *item; 2378 2379 if (!folderview->selected) return; 2380 2381 item = folderview_get_selected_item(folderview); 2382 cm_return_if_fail(item != NULL); 2383 cm_return_if_fail(item->folder != NULL); 2384 2385 prefs_folder_item_open(item); 2386 } 2387 2388 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder, 2389 FolderItem *to_folder, gboolean copy) 2390 { 2391 FolderItem *new_folder = NULL; 2392 gchar *buf; 2393 gint status; 2394 2395 cm_return_if_fail(folderview != NULL); 2396 cm_return_if_fail(from_folder != NULL); 2397 cm_return_if_fail(to_folder != NULL); 2398 2399 if (prefs_common.warn_dnd) { 2400 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"): 2401 _("Do you really want to make folder '%s' a subfolder of '%s'?"), 2402 from_folder->name, to_folder->name); 2403 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf, 2404 NULL, _("_No"), NULL, _("_Yes"), NULL, NULL, 2405 ALERTFOCUS_FIRST, TRUE, NULL, ALERT_QUESTION); 2406 g_free(buf); 2407 2408 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE) 2409 return; 2410 else if (status & G_ALERTDISABLE) 2411 prefs_common.warn_dnd = FALSE; 2412 } 2413 2414 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."), 2415 from_folder->name, to_folder->name); 2416 STATUSBAR_PUSH(folderview->mainwin, buf); 2417 g_free(buf); 2418 summary_clear_all(folderview->summaryview); 2419 folderview->opened = NULL; 2420 folderview->selected = NULL; 2421 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE); 2422 inc_lock(); 2423 main_window_cursor_wait(folderview->mainwin); 2424 2425 statusbar_verbosity_set(FALSE); 2426 folder_item_update_freeze(); 2427 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree)); 2428 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) { 2429 statusbar_verbosity_set(FALSE); 2430 main_window_cursor_normal(folderview->mainwin); 2431 STATUSBAR_POP(folderview->mainwin); 2432 folder_item_update_thaw(); 2433 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT); 2434 2435 folderview_sort_folders(folderview, 2436 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree), 2437 NULL, to_folder), new_folder->folder); 2438 folderview_select(folderview, new_folder); 2439 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree)); 2440 } else { 2441 statusbar_verbosity_set(FALSE); 2442 main_window_cursor_normal(folderview->mainwin); 2443 STATUSBAR_POP(folderview->mainwin); 2444 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree)); 2445 folder_item_update_thaw(); 2446 switch (status) { 2447 case F_MOVE_FAILED_DEST_IS_PARENT: 2448 alertpanel_error(_("Source and destination are the same.")); 2449 break; 2450 case F_MOVE_FAILED_DEST_IS_CHILD: 2451 alertpanel_error(copy ? _("Can't copy a folder to one of its children."): 2452 _("Can't move a folder to one of its children.")); 2453 break; 2454 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX: 2455 alertpanel_error(_("A folder cannot be moved between different mailboxes.")); 2456 break; 2457 default: 2458 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!")); 2459 break; 2460 } 2461 } 2462 inc_unlock(); 2463 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE); 2464 } 2465 2466 static gint folderview_clist_compare(GtkCMCList *clist, 2467 gconstpointer ptr1, gconstpointer ptr2) 2468 { 2469 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data; 2470 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data; 2471 2472 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it 2473 { 2474 return item1->order - item2->order; 2475 } 2476 2477 // if only one folder has an order it comes first 2478 if (item1->order > 0) 2479 { 2480 return -1; 2481 } 2482 if (item2->order > 0) 2483 { 2484 return 1; 2485 } 2486 2487 if (!item1->name) 2488 return (item2->name != NULL); 2489 if (!item2->name) 2490 return -1; 2491 2492 return g_utf8_collate(item1->name, item2->name); 2493 } 2494 2495 static gboolean folderview_update_folder(gpointer source, gpointer userdata) 2496 { 2497 FolderUpdateData *hookdata; 2498 FolderView *folderview; 2499 GtkWidget *ctree; 2500 2501 hookdata = source; 2502 folderview = (FolderView *) userdata; 2503 cm_return_val_if_fail(hookdata != NULL, FALSE); 2504 cm_return_val_if_fail(folderview != NULL, FALSE); 2505 2506 ctree = folderview->ctree; 2507 cm_return_val_if_fail(ctree != NULL, FALSE); 2508 2509 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM) 2510 folderview_create_folder_node(folderview, hookdata->item); 2511 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) { 2512 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), 2513 NULL, folder_item_parent(hookdata->item)); 2514 folderview_sort_folders(folderview, node, hookdata->folder); 2515 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) { 2516 GtkCMCTreeNode *node; 2517 2518 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item); 2519 if (node != NULL) { 2520 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node); 2521 if (folderview->selected == node) 2522 folderview->selected = NULL; 2523 if (folderview->opened == node) 2524 folderview->opened = NULL; 2525 } 2526 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) { 2527 /* do nothing, it's done by the ADD and REMOVE) */ 2528 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER)) 2529 folderview_set(folderview); 2530 2531 return FALSE; 2532 } 2533 2534 static gboolean folderview_dnd_scroll_cb(gpointer data) 2535 { 2536 FolderView *folderview = (FolderView *)data; 2537 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment( 2538 GTK_SCROLLED_WINDOW(folderview->scrolledwin)); 2539 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value; 2540 gint max = (int)gtk_adjustment_get_upper(pos) - 2541 (int)gtk_adjustment_get_page_size(pos); 2542 2543 if (folderview->scroll_value == 0) { 2544 folderview->scroll_timeout_id = 0; 2545 return FALSE; 2546 } 2547 2548 if (folderview->scroll_value > 0 && new_val > max) { 2549 new_val = max; 2550 } else if (folderview->scroll_value < 0 && new_val < 0) { 2551 new_val = 0; 2552 } 2553 gtk_adjustment_set_value(pos, new_val); 2554 2555 return TRUE; 2556 } 2557 2558 static void free_info(gpointer stuff, gpointer data) { g_free(stuff); } 2559 2560 void folderview_remove_item(FolderView *folderview, FolderItem *item) 2561 { 2562 g_return_if_fail(folderview != NULL); 2563 g_return_if_fail(item != NULL); 2564 2565 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree); 2566 g_return_if_fail(ctree != NULL); 2567 2568 GtkCMCTreeNode *node = 2569 gtk_cmctree_find_by_row_data(ctree, NULL, item); 2570 g_return_if_fail(node != NULL); 2571 2572 gtk_cmctree_remove_node(ctree, node); 2573 } 2574 2575 void folderview_freeze(FolderView *folderview) 2576 { 2577 if (folderview) 2578 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree)); 2579 } 2580 2581 void folderview_thaw(FolderView *folderview) 2582 { 2583 if (folderview) 2584 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree)); 2585 } 2586 2587 void folderview_grab_focus(FolderView *folderview) 2588 { 2589 if (folderview) 2590 gtk_widget_grab_focus(folderview->ctree); 2591 } 2592 2593 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction, 2594 gpointer data) 2595 { 2596 prefs_folder_column_open(); 2597 } 2598 2599 static gboolean folderview_header_button_pressed(GtkWidget *widget, 2600 GdkEvent *_event, 2601 gpointer user_data) 2602 { 2603 GdkEventButton *event = (GdkEventButton *)_event; 2604 FolderView *folderview = (FolderView *)user_data; 2605 2606 cm_return_val_if_fail(folderview != NULL, FALSE); 2607 2608 /* Only handle single button presses. */ 2609 if (event->type == GDK_2BUTTON_PRESS || 2610 event->type == GDK_3BUTTON_PRESS) 2611 return FALSE; 2612 2613 /* Handle right-click for context menu */ 2614 if (event->button == 3) { 2615 gtk_menu_popup_at_pointer(GTK_MENU(folderview->headerpopupmenu), NULL); 2616 return TRUE; 2617 } 2618 2619 return FALSE; 2620 }