2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2019 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <glib/gi18n.h>
23 #include <gdk/gdkkeysyms.h>
30 #include "mainwindow.h"
31 #include "folderview.h"
32 #include "summaryview.h"
33 #include "summary_search.h"
34 #include "inputdialog.h"
35 #include "manage_window.h"
36 #include "alertpanel.h"
38 #include "stock_pixmap.h"
42 #include "prefs_common.h"
43 #include "prefs_account.h"
44 #include "prefs_filtering.h"
45 #include "prefs_folder_item.h"
48 #include "foldersel.h"
50 #include "statusbar.h"
52 #include "folderutils.h"
53 #include "partial_download.h"
54 #include "prefs_folder_column.h"
55 #include "filtering.h"
56 #include "quicksearch.h"
60 #include "gtkcmctree.h"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *bold_style;
69 static GdkPixbuf *inboxxpm;
70 static GdkPixbuf *inboxhrmxpm;
71 static GdkPixbuf *inboxopenxpm;
72 static GdkPixbuf *inboxopenhrmxpm;
73 static GdkPixbuf *outboxxpm;
74 static GdkPixbuf *outboxhrmxpm;
75 static GdkPixbuf *outboxopenxpm;
76 static GdkPixbuf *outboxopenhrmxpm;
77 static GdkPixbuf *folderxpm;
78 static GdkPixbuf *folderhrmxpm;
79 static GdkPixbuf *folderopenxpm;
80 static GdkPixbuf *folderopenhrmxpm;
81 static GdkPixbuf *trashopenxpm;
82 static GdkPixbuf *trashopenhrmxpm;
83 static GdkPixbuf *trashxpm;
84 static GdkPixbuf *trashhrmxpm;
85 static GdkPixbuf *queuexpm;
86 static GdkPixbuf *queuehrmxpm;
87 static GdkPixbuf *queueopenxpm;
88 static GdkPixbuf *queueopenhrmxpm;
89 static GdkPixbuf *draftsxpm;
90 static GdkPixbuf *draftsopenxpm;
91 static GdkPixbuf *foldersubsxpm;
92 static GdkPixbuf *foldersubsopenxpm;
93 static GdkPixbuf *foldernoselectxpm;
94 static GdkPixbuf *foldernoselectopenxpm;
96 static GdkPixbuf *m_inboxxpm;
97 static GdkPixbuf *m_inboxhrmxpm;
98 static GdkPixbuf *m_inboxopenxpm;
99 static GdkPixbuf *m_inboxopenhrmxpm;
100 static GdkPixbuf *m_outboxxpm;
101 static GdkPixbuf *m_outboxhrmxpm;
102 static GdkPixbuf *m_outboxopenxpm;
103 static GdkPixbuf *m_outboxopenhrmxpm;
104 static GdkPixbuf *m_folderxpm;
105 static GdkPixbuf *m_folderhrmxpm;
106 static GdkPixbuf *m_folderopenxpm;
107 static GdkPixbuf *m_folderopenhrmxpm;
108 static GdkPixbuf *m_trashopenxpm;
109 static GdkPixbuf *m_trashopenhrmxpm;
110 static GdkPixbuf *m_trashxpm;
111 static GdkPixbuf *m_trashhrmxpm;
112 static GdkPixbuf *m_queuexpm;
113 static GdkPixbuf *m_queuehrmxpm;
114 static GdkPixbuf *m_queueopenxpm;
115 static GdkPixbuf *m_queueopenhrmxpm;
116 static GdkPixbuf *m_draftsxpm;
117 static GdkPixbuf *m_draftsopenxpm;
118 static GdkPixbuf *m_foldersubsxpm;
119 static GdkPixbuf *m_foldernoselectxpm;
121 static GdkPixbuf *newxpm;
122 static GdkPixbuf *unreadxpm;
123 static GdkPixbuf *readxpm;
125 static void folderview_select_node (FolderView *folderview,
126 GtkCMCTreeNode *node);
127 static void folderview_set_folders (FolderView *folderview);
128 static void folderview_sort_folders (FolderView *folderview,
129 GtkCMCTreeNode *root,
131 static void folderview_append_folder (FolderView *folderview,
133 static void folderview_update_node (FolderView *folderview,
134 GtkCMCTreeNode *node);
136 static gint folderview_clist_compare (GtkCMCList *clist,
140 /* callback functions */
141 static gboolean folderview_button_pressed (GtkWidget *ctree,
142 GdkEventButton *event,
143 FolderView *folderview);
144 static gboolean folderview_button_released (GtkWidget *ctree,
145 GdkEventButton *event,
146 FolderView *folderview);
147 static gboolean folderview_key_pressed (GtkWidget *widget,
149 FolderView *folderview);
150 static void folderview_selected (GtkCMCTree *ctree,
153 FolderView *folderview);
154 static void folderview_tree_expanded (GtkCMCTree *ctree,
155 GtkCMCTreeNode *node,
156 FolderView *folderview);
157 static void folderview_tree_collapsed (GtkCMCTree *ctree,
158 GtkCMCTreeNode *node,
159 FolderView *folderview);
160 static void folderview_popup_close (GtkMenuShell *menu_shell,
161 FolderView *folderview);
162 static void folderview_col_resized (GtkCMCList *clist,
165 FolderView *folderview);
167 static void mark_all_read_unread_handler (GtkAction *action,
172 static void mark_all_read_cb (GtkAction *action,
174 static void mark_all_unread_cb (GtkAction *action,
176 static void mark_all_read_recursive_cb (GtkAction *action,
178 static void mark_all_unread_recursive_cb (GtkAction *action,
181 static void folderview_empty_trash_cb (GtkAction *action,
184 static void folderview_send_queue_cb (GtkAction *action,
187 static void folderview_search_cb (GtkAction *action,
189 static void folderview_run_processing_cb(GtkAction *action,
192 static void folderview_property_cb (GtkAction *action,
195 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
196 GdkDragContext *context,
200 FolderView *folderview);
201 static void folderview_drag_leave_cb (GtkWidget *widget,
202 GdkDragContext *context,
204 FolderView *folderview);
205 static void folderview_drag_received_cb (GtkWidget *widget,
206 GdkDragContext *drag_context,
209 GtkSelectionData *data,
212 FolderView *folderview);
214 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
215 FolderView *folderview);
217 static void folderview_drag_data_get (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 GtkSelectionData *selection_data,
222 FolderView *folderview);
223 static void folderview_drag_end_cb (GtkWidget *widget,
224 GdkDragContext *drag_context,
225 FolderView *folderview);
227 static void folderview_create_folder_node (FolderView *folderview,
229 static gboolean folderview_update_folder (gpointer source,
231 static gboolean folderview_update_item_claws (gpointer source,
233 static void folderview_processing_cb(GtkAction *action, gpointer data);
234 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
235 GdkEventButton *event);
236 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
238 static gboolean folderview_header_button_pressed(GtkWidget *widget,
242 GHashTable *folderview_popups;
244 static GtkActionEntry folderview_common_popup_entries[] =
246 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
247 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
248 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
249 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
250 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
251 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
252 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
253 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
254 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
255 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
256 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
257 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
261 static GtkActionEntry folderview_header_popup_entries[] =
263 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL },
264 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) }
267 GtkTargetEntry folderview_drag_types[] =
269 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
270 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
273 void folderview_initialize(void)
275 FolderViewPopup *fpopup;
277 fpopup = g_new0(FolderViewPopup, 1);
279 fpopup->klass = "common";
280 fpopup->path = "<CommonFolder>";
281 fpopup->entries = folderview_common_popup_entries;
282 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
283 fpopup->set_sensitivity = NULL;
285 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
286 g_hash_table_insert(folderview_popups, "common", fpopup);
289 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
291 FolderViewPopup *fpopup_common;
292 GtkActionGroup *action_group;
294 action_group = cm_menu_create_action_group(
296 fpopup->entries, fpopup->n_entries,
297 (gpointer)folderview);
299 if (fpopup->toggle_entries)
300 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
301 fpopup->n_toggle_entries,
302 (gpointer)folderview);
303 if (fpopup->radio_entries)
304 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
305 fpopup->n_radio_entries, fpopup->radio_default,
306 G_CALLBACK(fpopup->radio_callback),
307 (gpointer)folderview);
309 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
310 if (fpopup_common != fpopup) {
311 gtk_action_group_add_actions(action_group, fpopup_common->entries,
312 fpopup_common->n_entries,
313 (gpointer)folderview);
314 if (fpopup_common->toggle_entries)
315 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
316 fpopup_common->n_toggle_entries,
317 (gpointer)folderview);
318 if (fpopup_common->radio_entries)
319 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
320 fpopup_common->n_radio_entries, fpopup_common->radio_default,
321 G_CALLBACK(fpopup_common->radio_callback),
322 (gpointer)folderview);
328 static void create_action_groups(gpointer key, gpointer value, gpointer data)
330 FolderView *folderview = data;
331 FolderViewPopup *fpopup = value;
332 GtkActionGroup *group;
334 group = create_action_group(folderview, fpopup);
335 g_hash_table_insert(folderview->popups, fpopup->klass, group);
338 static void folderview_column_set_titles(FolderView *folderview)
340 GtkWidget *ctree = folderview->ctree;
341 GtkWidget *label_folder;
342 GtkWidget *label_new;
343 GtkWidget *label_unread;
344 GtkWidget *label_total;
345 GtkWidget *hbox_folder;
347 GtkWidget *hbox_unread;
348 GtkWidget *hbox_total;
349 gint *col_pos = folderview->col_pos;
351 debug_print("setting titles...\n");
352 gtk_widget_realize(folderview->ctree);
353 gtk_widget_show_all(folderview->scrolledwin);
355 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
356 * instead text (text overflows making them unreadable and ugly) */
357 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
358 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
359 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
361 label_folder = gtk_label_new(_("Folder"));
362 label_new = gtk_image_new_from_pixbuf(newxpm);
363 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
364 label_total = gtk_image_new_from_pixbuf(readxpm);
366 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
368 hbox_folder = gtk_hbox_new(FALSE, 4);
369 hbox_new = gtk_hbox_new(FALSE, 4);
370 hbox_unread = gtk_hbox_new(FALSE, 4);
371 hbox_total = gtk_hbox_new(FALSE, 4);
374 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
375 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
376 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
377 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
378 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
379 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
380 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
381 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
383 gtk_widget_show_all(hbox_folder);
384 gtk_widget_show_all(hbox_new);
385 gtk_widget_show_all(hbox_unread);
386 gtk_widget_show_all(hbox_total);
389 gtk_widget_set_size_request(hbox_new, -1, 20);
390 gtk_widget_set_size_request(hbox_unread, -1, 20);
391 gtk_widget_set_size_request(hbox_total, -1, 20);
394 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
395 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
396 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
397 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
403 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
404 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
405 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
408 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
410 FolderView *folderview = (FolderView *)data;
411 GdkEventButton event;
412 if (folderview_get_selected_item(folderview) == NULL)
416 event.time = gtk_get_current_event_time();
418 folderview_set_sens_and_popup_menu(folderview, -1,
425 static GtkWidget *folderview_ctree_create(FolderView *folderview)
429 FolderColumnState *col_state;
430 FolderColumnType type;
431 gchar *titles[N_FOLDER_COLS];
433 GtkWidget *scrolledwin = folderview->scrolledwin;
435 debug_print("creating tree...\n");
436 memset(titles, 0, sizeof(titles));
438 col_state = prefs_folder_column_get_config();
439 memset(titles, 0, sizeof(titles));
441 col_pos = folderview->col_pos;
443 for (i = 0; i < N_FOLDER_COLS; i++) {
444 folderview->col_state[i] = col_state[i];
445 type = col_state[i].type;
449 titles[col_pos[F_COL_FOLDER]] = _("Folder");
450 titles[col_pos[F_COL_NEW]] = _("New");
451 titles[col_pos[F_COL_UNREAD]] = _("Unread");
452 /* TRANSLATORS: This in Number sign in American style */
453 titles[col_pos[F_COL_TOTAL]] = _("#");
455 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
458 if (prefs_common.show_col_headers == FALSE)
459 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
462 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
463 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
465 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
466 col_pos[F_COL_UNREAD],
468 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
469 col_pos[F_COL_TOTAL],
471 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
472 GTK_CMCTREE_EXPANDER_TRIANGLE);
474 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
475 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
477 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
478 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
480 /* don't let title buttons take key focus */
481 for (i = 0; i < N_FOLDER_COLS; i++) {
482 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
483 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
484 prefs_common.folder_col_size[i]);
485 gtk_cmclist_set_column_visibility
486 (GTK_CMCLIST(ctree), i, col_state[i].visible);
488 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button),
489 "button-press-event",
490 G_CALLBACK(folderview_header_button_pressed),
494 g_signal_connect(G_OBJECT(ctree), "key_press_event",
495 G_CALLBACK(folderview_key_pressed),
497 g_signal_connect(G_OBJECT(ctree), "button_press_event",
498 G_CALLBACK(folderview_button_pressed),
500 g_signal_connect(G_OBJECT(ctree), "popup-menu",
501 G_CALLBACK(folderview_popup_menu), folderview);
502 g_signal_connect(G_OBJECT(ctree), "button_release_event",
503 G_CALLBACK(folderview_button_released),
505 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
506 G_CALLBACK(folderview_selected), folderview);
508 /* drag-n-dropping folders on maemo is impractical as this
509 * opens the folder almost everytime */
510 g_signal_connect(G_OBJECT(ctree), "start_drag",
511 G_CALLBACK(folderview_start_drag), folderview);
513 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
514 G_CALLBACK(folderview_drag_data_get),
517 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
518 G_CALLBACK(folderview_tree_expanded),
520 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
521 G_CALLBACK(folderview_tree_collapsed),
524 g_signal_connect(G_OBJECT(ctree), "resize_column",
525 G_CALLBACK(folderview_col_resized),
529 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
530 folderview_drag_types, 2,
531 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
532 g_signal_connect(G_OBJECT(ctree), "drag_motion",
533 G_CALLBACK(folderview_drag_motion_cb),
535 g_signal_connect(G_OBJECT(ctree), "drag_leave",
536 G_CALLBACK(folderview_drag_leave_cb),
538 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
539 G_CALLBACK(folderview_drag_received_cb),
541 g_signal_connect(G_OBJECT(ctree), "drag_end",
542 G_CALLBACK(folderview_drag_end_cb),
545 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
550 void folderview_set_column_order(FolderView *folderview)
552 GtkWidget *ctree = folderview->ctree;
553 FolderItem *item = folderview_get_selected_item(folderview);
554 FolderItem *sel_item = NULL, *op_item = NULL;
555 GtkWidget *scrolledwin = folderview->scrolledwin;
557 if (folderview->drag_timer_id != 0) {
558 g_source_remove(folderview->drag_timer_id);
559 folderview->drag_timer_id = 0;
561 if (folderview->deferred_refresh_id != 0) {
562 g_source_remove(folderview->deferred_refresh_id);
563 folderview->deferred_refresh_id = 0;
565 if (folderview->scroll_timeout_id != 0) {
566 g_source_remove(folderview->scroll_timeout_id);
567 folderview->scroll_timeout_id = 0;
569 if (folderview->postpone_select_id != 0) {
570 g_source_remove(folderview->postpone_select_id);
571 folderview->postpone_select_id = 0;
574 if (folderview->selected)
575 sel_item = folderview_get_selected_item(folderview);
576 if (folderview->opened)
577 op_item = folderview_get_opened_item(folderview);
579 debug_print("recreating tree...\n");
580 gtk_widget_destroy(folderview->ctree);
583 folderview->ctree = ctree = folderview_ctree_create(folderview);
584 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
585 GTK_CMCLIST(ctree)->hadjustment);
586 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
587 GTK_CMCLIST(ctree)->vadjustment);
588 gtk_widget_show(ctree);
591 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
593 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
595 folderview_set(folderview);
596 folderview_column_set_titles(folderview);
598 folderview_select(folderview,item);
601 FolderView *folderview_create(MainWindow *mainwin)
603 FolderView *folderview;
604 GtkWidget *scrolledwin;
607 debug_print("Creating folder view...\n");
608 folderview = g_new0(FolderView, 1);
610 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
611 gtk_scrolled_window_set_policy
612 (GTK_SCROLLED_WINDOW(scrolledwin),
613 GTK_POLICY_AUTOMATIC,
614 prefs_common.folderview_vscrollbar_policy);
615 gtk_widget_set_size_request(scrolledwin,
616 prefs_common.folderview_width,
617 prefs_common.folderview_height);
619 folderview->scrolledwin = scrolledwin;
620 ctree = folderview_ctree_create(folderview);
622 /* create popup factories */
623 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
624 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
626 gtk_action_group_add_actions(mainwin->action_group,
627 folderview_header_popup_entries,
628 G_N_ELEMENTS(folderview_header_popup_entries),
629 (gpointer)folderview);
631 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU)
632 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
634 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
635 gtk_ui_manager_get_widget(mainwin->ui_manager,
636 "/Menus/FolderViewHeaderPopup") ));
638 folderview->ctree = ctree;
640 folderview->folder_update_callback_id =
641 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
642 folderview->folder_item_update_callback_id =
643 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
645 gtk_widget_show_all(scrolledwin);
647 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
648 folderview_list = g_list_append(folderview_list, folderview);
650 folderview->drag_timer_id = 0;
651 folderview->deferred_refresh_id = 0;
652 folderview->scroll_timeout_id = 0;
653 folderview->postpone_select_id = 0;
658 static void folderview_set_fonts(FolderView *folderview)
660 PangoFontDescription *font_desc;
661 GtkWidget *ctree = folderview->ctree;
663 font_desc = pango_font_description_from_string(NORMAL_FONT);
665 gtk_widget_modify_font(ctree, font_desc);
666 pango_font_description_free(font_desc);
670 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
672 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
673 font_desc = pango_font_description_from_string(NORMAL_FONT);
675 pango_font_description_free(bold_style->font_desc);
676 bold_style->font_desc = font_desc;
678 pango_font_description_set_weight
679 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
681 font_desc = pango_font_description_from_string(BOLD_FONT);
683 pango_font_description_free(bold_style->font_desc);
684 bold_style->font_desc = font_desc;
690 void folderview_init(FolderView *folderview)
692 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
693 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
694 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
695 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
696 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
697 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
698 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
699 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
700 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
701 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
702 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
703 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
704 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
705 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
706 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
707 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
708 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
709 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
710 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
711 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
712 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
713 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
714 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
715 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
716 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
717 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
719 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
720 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
721 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
722 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
723 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
724 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
725 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
726 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
727 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
728 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
729 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
730 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
731 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
732 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
733 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
734 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
735 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
736 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
737 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
738 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
739 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
740 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
741 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
742 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
744 folderview_set_fonts(folderview);
747 static gboolean folderview_defer_set(gpointer data)
749 FolderView *folderview = (FolderView *)data;
750 MainWindow *mainwin = folderview->mainwin;
754 if (mainwin->lock_count)
757 debug_print("doing deferred folderview_set now\n");
758 folderview_set(folderview);
760 folderview->deferred_refresh_id = 0;
764 void folderview_set(FolderView *folderview)
766 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
767 MainWindow *mainwin = folderview->mainwin;
768 FolderItem *sel_item = NULL, *op_item = NULL;
773 if (mainwin->lock_count) {
774 if (folderview->deferred_refresh_id == 0)
775 folderview->deferred_refresh_id =
776 g_timeout_add(500, folderview_defer_set, folderview);
777 debug_print("deferred folderview_set\n");
782 debug_print("Setting folder info...\n");
783 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
785 main_window_cursor_wait(mainwin);
787 if (folderview->selected)
788 sel_item = folderview_get_selected_item(folderview);
789 if (folderview->opened)
790 op_item = folderview_get_opened_item(folderview);
792 folderview->selected = NULL;
793 folderview->opened = NULL;
795 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
796 gtk_cmclist_clear(GTK_CMCLIST(ctree));
798 folderview_set_folders(folderview);
801 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
803 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
805 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
806 main_window_cursor_normal(mainwin);
807 STATUSBAR_POP(mainwin);
811 void folderview_set_all(void)
815 for (list = folderview_list; list != NULL; list = list->next)
816 folderview_set((FolderView *)list->data);
819 void folderview_select(FolderView *folderview, FolderItem *item)
821 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
822 GtkCMCTreeNode *node;
823 GtkCMCTreeNode *old_selected = folderview->selected;
827 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
828 if (node) folderview_select_node(folderview, node);
830 if (old_selected != node)
831 folder_update_op_count();
834 static void mark_all_read_cb(GtkAction *action, gpointer data)
836 mark_all_read_unread_handler(action, data, FALSE, TRUE);
839 static void mark_all_unread_cb(GtkAction *action, gpointer data)
841 mark_all_read_unread_handler(action, data, FALSE, FALSE);
844 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
846 mark_all_read_unread_handler(action, data, TRUE, TRUE);
849 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
851 mark_all_read_unread_handler(action, data, TRUE, FALSE);
854 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
855 gboolean recursive, gboolean read)
857 FolderView *folderview = (FolderView *)data;
863 item = folderview_get_selected_item(folderview);
868 title = _("Mark all as read");
869 message = recursive? _("Do you really want to mark all mails in this "
870 "folder and its sub-folders as read?") :
871 _("Do you really want to mark all mails in this "
874 title = _("Mark all as unread");
875 message = recursive? _("Do you really want to mark all mails in this "
876 "folder and its sub-folders as unread?") :
877 _("Do you really want to mark all mails in this "
878 "folder as unread?");
880 if (prefs_common.ask_mark_all_read) {
881 val = alertpanel_full(title, message,
882 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
883 TRUE, NULL, ALERT_QUESTION);
885 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
887 else if (val & G_ALERTDISABLE)
888 prefs_common.ask_mark_all_read = FALSE;
891 folder_item_update_freeze();
892 if (folderview->summaryview->folder_item != item && !recursive)
893 summary_lock(folderview->summaryview);
895 summary_freeze(folderview->summaryview);
899 folderutils_mark_all_read_recursive(item, TRUE);
901 folderutils_mark_all_read(item, TRUE);
904 folderutils_mark_all_read_recursive(item, FALSE);
906 folderutils_mark_all_read(item, FALSE);
908 if (folderview->summaryview->folder_item != item && !recursive)
909 summary_unlock(folderview->summaryview);
911 summary_thaw(folderview->summaryview);
912 folder_item_update_thaw();
915 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
917 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
919 cm_return_if_fail(node != NULL);
921 if (folderview->open_folder) {
925 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
926 gtkut_ctree_expand_parent_all(ctree, node);
928 folderview->open_folder = TRUE;
929 gtkut_ctree_set_focus_row(ctree, node);
930 gtk_cmctree_select(ctree, node);
931 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
932 if ((folderview->summaryview->folder_item &&
933 folderview->summaryview->folder_item->total_msgs > 0) ||
934 prefs_common.layout_mode == SMALL_LAYOUT)
935 summary_select_node(folderview->summaryview,
936 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
938 gtk_widget_grab_focus(folderview->ctree);
941 void folderview_unselect(FolderView *folderview)
943 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
945 (GTK_CMCTREE(folderview->ctree), folderview->opened);
947 folderview->selected = folderview->opened = NULL;
950 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
951 GtkCMCTreeNode *node,
957 node = gtkut_ctree_node_next(ctree, node);
959 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
961 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
962 item = gtk_cmctree_node_get_row_data(ctree, node);
965 if (item->stype == F_TRASH || item->stype == F_DRAFT)
969 if(item->unread_msgs > 0)
973 if(item->new_msgs > 0)
977 if(item->marked_msgs > 0)
981 if(item->total_msgs > 0)
990 void folderview_select_next_with_flag(FolderView *folderview,
993 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
994 GtkCMCTreeNode *node = NULL;
995 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
999 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1002 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1005 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1008 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1012 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1014 folderview_select_node(folderview, node);
1018 if (!folderview->opened ||
1019 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1023 /* search again from the first node */
1024 node = folderview_find_next_with_flag(ctree, NULL, flag);
1026 folderview_select_node(folderview, node);
1029 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1032 FolderItem *folderview_get_selected_item(FolderView *folderview)
1034 g_return_val_if_fail(folderview != NULL, NULL);
1035 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1037 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1039 if (!folderview->selected) return NULL;
1040 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1043 FolderItem *folderview_get_opened_item(FolderView *folderview)
1045 g_return_val_if_fail(folderview != NULL, NULL);
1046 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1048 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1050 if (!folderview->opened) return NULL;
1051 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1054 static void folderview_set_folders(FolderView *folderview)
1057 list = folder_get_list();
1059 for (; list != NULL; list = list->next) {
1060 folderview_append_folder(folderview, FOLDER(list->data));
1064 static gchar *get_scan_str(FolderItem *item)
1067 return g_strdup_printf(_("Scanning folder %s/%s..."),
1068 item->folder->name, item->path);
1070 return g_strdup_printf(_("Scanning folder %s..."),
1071 item->folder->name);
1073 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1077 for (list = folderview_list; list != NULL; list = list->next) {
1078 FolderView *folderview = (FolderView *)list->data;
1079 MainWindow *mainwin = folderview->mainwin;
1080 gchar *str = get_scan_str(item);
1082 STATUSBAR_PUSH(mainwin, str);
1083 STATUSBAR_POP(mainwin);
1088 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1091 MainWindow *mainwin = mainwindow_get_mainwindow();
1092 FolderView *folderview = NULL;
1093 GtkAdjustment *pos = NULL;
1096 cm_return_if_fail(folder != NULL);
1098 if (!folder->klass->scan_tree) return;
1101 alertpanel_full(_("Rebuild folder tree"),
1102 _("Rebuilding the folder tree will remove "
1103 "local caches. Do you want to continue?"),
1104 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
1105 FALSE, NULL, ALERT_WARNING)
1106 != G_ALERTALTERNATE) {
1112 window = label_window_create(_("Rebuilding folder tree..."));
1114 window = label_window_create(_("Scanning folder tree..."));
1117 folderview = mainwin->folderview;
1120 pos = gtk_scrolled_window_get_vadjustment(
1121 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1122 height = gtk_adjustment_get_value(pos);
1125 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1126 folder_scan_tree(folder, rebuild);
1127 folder_set_ui_func(folder, NULL, NULL);
1129 folderview_set_all();
1132 pos = gtk_scrolled_window_get_vadjustment(
1133 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1134 gtk_adjustment_set_value(pos, height);
1135 gtk_adjustment_changed(pos);
1137 label_window_destroy(window);
1141 /** folderview_check_new()
1142 * Scan and update the folder and return the
1143 * count the number of new messages since last check.
1144 * \param folder the folder to check for new messages
1145 * \return the number of new messages since last check
1147 gint folderview_check_new(Folder *folder)
1151 FolderView *folderview;
1153 GtkCMCTreeNode *node;
1155 gint former_new_msgs = 0;
1156 gint former_new = 0, former_unread = 0, former_total;
1158 for (list = folderview_list; list != NULL; list = list->next) {
1159 folderview = (FolderView *)list->data;
1160 ctree = GTK_CMCTREE(folderview->ctree);
1161 folderview->scanning_folder = folder;
1163 main_window_lock(folderview->mainwin);
1165 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1166 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1168 item = gtk_cmctree_node_get_row_data(ctree, node);
1169 if (!item || !item->path || !item->folder) continue;
1170 if (item->no_select) continue;
1171 if (folder && folder != item->folder) continue;
1172 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1173 if (!item->prefs->newmailcheck) continue;
1174 if (item->processing_pending == TRUE) {
1175 debug_print("skipping %s, processing pending\n",
1176 item->path ? item->path : item->name);
1179 if (item->scanning != ITEM_NOT_SCANNING) {
1180 debug_print("skipping %s, scanning\n",
1181 item->path ? item->path : item->name);
1185 str = get_scan_str(item);
1187 STATUSBAR_PUSH(folderview->mainwin, str);
1191 folderview_scan_tree_func(item->folder, item, NULL);
1192 former_new = item->new_msgs;
1193 former_unread = item->unread_msgs;
1194 former_total = item->total_msgs;
1196 if (item->folder->klass->scan_required &&
1197 (item->folder->klass->scan_required(item->folder, item) ||
1198 item->folder->inbox == item ||
1199 item->opened == TRUE ||
1200 item->processing_pending == TRUE)) {
1201 if (folder_item_scan(item) < 0) {
1203 summaryview_unlock(folderview->summaryview, item);
1204 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1205 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1206 item->path ? item->path:item->name);
1207 STATUSBAR_POP(folderview->mainwin);
1209 } else if (!FOLDER_IS_LOCAL(folder)) {
1210 STATUSBAR_POP(folderview->mainwin);
1215 } else if (!item->folder->klass->scan_required) {
1216 if (folder_item_scan(item) < 0) {
1217 summaryview_unlock(folderview->summaryview, item);
1218 if (folder && !FOLDER_IS_LOCAL(folder)) {
1219 STATUSBAR_POP(folderview->mainwin);
1224 if (former_new != item->new_msgs ||
1225 former_unread != item->unread_msgs ||
1226 former_total != item->total_msgs)
1227 folderview_update_node(folderview, node);
1229 new_msgs += item->new_msgs;
1230 former_new_msgs += former_new;
1231 STATUSBAR_POP(folderview->mainwin);
1233 folderview->scanning_folder = NULL;
1234 main_window_unlock(folderview->mainwin);
1238 folder_write_list();
1239 /* Number of new messages since last check is the just the difference
1240 * between former_new_msgs and new_msgs. If new_msgs is less than
1241 * former_new_msgs, that would mean another session accessed the folder
1242 * and the result is not well defined.
1244 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1248 void folderview_check_new_all(void)
1252 FolderView *folderview;
1254 folderview = (FolderView *)folderview_list->data;
1257 main_window_lock(folderview->mainwin);
1258 window = label_window_create
1259 (_("Checking for new messages in all folders..."));
1261 list = folder_get_list();
1262 for (; list != NULL; list = list->next) {
1263 Folder *folder = list->data;
1265 folderview_check_new(folder);
1268 folder_write_list();
1269 folderview_set_all();
1271 label_window_destroy(window);
1272 main_window_unlock(folderview->mainwin);
1276 static gboolean folderview_have_children_sub(FolderView *folderview,
1282 if (!item || !item->folder || !item->folder->node)
1285 node = item->folder->node;
1287 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1288 node = node->children;
1290 if (in_sub && item->total_msgs > 0) {
1294 while (node != NULL) {
1295 if (node && node->data) {
1296 FolderItem *next_item = (FolderItem*) node->data;
1298 if (folderview_have_children_sub(folderview,
1307 static gboolean folderview_have_children(FolderView *folderview,
1310 return folderview_have_children_sub(folderview, item, FALSE);
1313 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1319 if (!item || !item->folder || !item->folder->node)
1322 node = item->folder->node;
1324 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1325 node = node->children;
1328 (item->new_msgs > 0 ||
1329 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1333 while (node != NULL) {
1334 if (node && node->data) {
1335 FolderItem *next_item = (FolderItem*) node->data;
1337 if (folderview_have_new_children_sub(folderview,
1346 static gboolean folderview_have_new_children(FolderView *folderview,
1349 return folderview_have_new_children_sub(folderview, item, FALSE);
1352 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1358 if (!item || !item->folder || !item->folder->node)
1361 node = item->folder->node;
1363 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1364 node = node->children;
1367 (item->unread_msgs > 0 ||
1368 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1372 while (node != NULL) {
1373 if (node && node->data) {
1374 FolderItem *next_item = (FolderItem*) node->data;
1376 if (folderview_have_unread_children_sub(folderview,
1386 static gboolean folderview_have_unread_children(FolderView *folderview,
1389 return folderview_have_unread_children_sub(folderview, item, FALSE);
1392 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1398 if (!item || !item->folder || !item->folder->node) {
1402 node = item->folder->node;
1404 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1405 node = node->children;
1408 (((item->total_msgs > 0) &&
1409 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1413 while (node != NULL) {
1414 if (node && node->data) {
1415 FolderItem *next_item = (FolderItem*) node->data;
1417 if (folderview_have_read_children_sub(folderview,
1428 static gboolean folderview_have_read_children(FolderView *folderview,
1431 return folderview_have_read_children_sub(folderview, item, FALSE);
1434 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1440 if (!item || !item->folder || !item->folder->node)
1443 node = item->folder->node;
1445 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1446 node = node->children;
1448 if (in_sub && item->search_match){
1452 while (node != NULL) {
1453 if (node && node->data) {
1454 FolderItem *next_item = (FolderItem*) node->data;
1456 if (folderview_have_matching_children_sub(folderview,
1466 static gboolean folderview_have_matching_children(FolderView *folderview,
1469 return folderview_have_matching_children_sub(folderview, item, FALSE);
1472 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1478 if (!item || !item->folder || !item->folder->node)
1481 node = item->folder->node;
1483 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1484 node = node->children;
1486 if (item->marked_msgs != 0) {
1490 while (node != NULL) {
1491 if (node && node->data) {
1492 FolderItem *next_item = (FolderItem*) node->data;
1494 if (folderview_have_marked_children_sub(folderview,
1503 static gboolean folderview_have_marked_children(FolderView *folderview,
1506 return folderview_have_marked_children_sub(folderview, item, FALSE);
1509 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1511 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1512 GtkStyle *style = NULL, *prev_style;
1514 GdkPixbuf *xpm, *openxpm;
1515 static GdkPixbuf *searchicon;
1516 gboolean mark = FALSE;
1519 gboolean add_unread_mark;
1520 gboolean add_sub_match_mark;
1521 gboolean use_bold, use_color;
1522 gint *col_pos = folderview->col_pos;
1523 SpecialFolderItemType stype;
1526 item = gtk_cmctree_node_get_row_data(ctree, node);
1527 cm_return_if_fail(item != NULL);
1529 if (!GTK_CMCTREE_ROW(node)->expanded)
1530 mark = folderview_have_marked_children(folderview, item);
1532 mark = (item->marked_msgs != 0);
1534 stype = item->stype;
1535 if (stype == F_NORMAL) {
1536 if (folder_has_parent_of_type(item, F_TRASH))
1538 else if (folder_has_parent_of_type(item, F_DRAFT))
1540 else if (folder_has_parent_of_type(item, F_OUTBOX))
1542 else if (folder_has_parent_of_type(item, F_QUEUE))
1547 if (item->hide_read_msgs || item->hide_read_threads) {
1548 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1549 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1551 xpm = mark?m_inboxxpm:inboxxpm;
1552 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1556 if (item->hide_read_msgs || item->hide_read_threads) {
1557 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1558 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1560 xpm = mark?m_outboxxpm:outboxxpm;
1561 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1565 if (item->hide_read_msgs || item->hide_read_threads) {
1566 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1567 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1569 xpm = mark?m_queuexpm:queuexpm;
1570 openxpm = mark?m_queueopenxpm:queueopenxpm;
1574 if (item->hide_read_msgs || item->hide_read_threads) {
1575 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1576 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1578 xpm = mark?m_trashxpm:trashxpm;
1579 openxpm = mark?m_trashopenxpm:trashopenxpm;
1583 xpm = mark?m_draftsxpm:draftsxpm;
1584 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1588 FOLDER_TYPE(item->folder) == F_IMAP &&
1589 item->folder->account->imap_subsonly) {
1590 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1591 openxpm = foldersubsopenxpm;
1592 } else if (item->no_select) {
1593 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1594 openxpm = foldernoselectopenxpm;
1595 } else if (item->hide_read_msgs || item->hide_read_threads) {
1596 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1597 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1599 xpm = mark?m_folderxpm:folderxpm;
1600 openxpm = mark?m_folderopenxpm:folderopenxpm;
1604 name = folder_item_get_name(item);
1606 if (!GTK_CMCTREE_ROW(node)->expanded) {
1607 add_unread_mark = folderview_have_unread_children(
1609 add_sub_match_mark = folderview_have_matching_children(
1612 add_unread_mark = FALSE;
1613 add_sub_match_mark = FALSE;
1616 if (item->search_match) {
1618 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1621 xpm = openxpm = searchicon;
1625 if (prefs_common.display_folder_unread) {
1626 if (folder_has_parent_of_type(item, F_QUEUE)) {
1627 /* only total_msgs matters here */
1628 if (item->total_msgs > 0) {
1629 /* show total number (should be equal to the unread number)
1631 str = g_strdup_printf("%s (%d%s%s)",
1632 name, item->total_msgs,
1633 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1634 (item->unreadmarked_msgs > 0) ? "!" : "");
1637 if (prefs_common.display_folder_unread == 1) {
1638 if (item->unread_msgs > 0) {
1639 /* show unread number and signs */
1640 str = g_strdup_printf("%s (%d%s%s)",
1641 name, item->unread_msgs,
1642 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1643 (item->unreadmarked_msgs > 0) ? "!" : "");
1646 if (item->total_msgs > 0) {
1647 /* show unread number, total number and signs if any */
1648 str = g_strdup_printf("%s (%d/%d%s%s)",
1649 name, item->unread_msgs, item->total_msgs,
1650 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1651 (item->unreadmarked_msgs > 0) ? "!" : "");
1655 if ((str == NULL) &&
1656 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1657 /* no unread/total numbers, but at least one sign */
1658 str = g_strdup_printf("%s (%s%s)",
1660 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1661 (item->unreadmarked_msgs > 0) ? "!" : "");
1665 /* last fallback, folder name only or with +! sign */
1666 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1667 str = g_strdup_printf("%s%s",
1669 } else if (item->unreadmarked_msgs > 0) {
1670 str = g_strdup_printf("%s%s",
1672 } else if (add_sub_match_mark) {
1673 str = g_strdup_printf("%s%s",
1676 str = g_strdup_printf("%s", name);
1679 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1681 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1685 if (!folder_item_parent(item)) {
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1687 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1691 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1692 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1695 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1696 folder_has_parent_of_type(item, F_TRASH)) {
1697 use_bold = use_color = FALSE;
1698 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1699 GSList *list = folder_item_get_msg_list(item);
1701 use_bold = use_color = FALSE;
1702 for (cur = list; cur; cur = cur->next) {
1703 MsgInfo *msginfo = (MsgInfo *)cur->data;
1704 if (!MSG_IS_DELETED(msginfo->flags)) {
1705 /* highlight queue folder if there are any messages */
1706 use_bold = use_color = TRUE;
1710 if (!GTK_CMCTREE_ROW(node)->expanded &&
1711 use_bold == FALSE &&
1712 folderview_have_children(folderview, item))
1713 use_bold = use_color = TRUE;
1714 procmsg_msg_list_free(list);
1716 /* if unread messages exist or target folder is set, print with bold font */
1717 use_bold = (item->unread_msgs > 0 || item->new_msgs > 0 || item->op_count > 0)
1719 /* if new messages exist, print with colored letter */
1721 (item->new_msgs > 0) ||
1723 folderview_have_new_children(folderview, item));
1726 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1730 if (item->op_count > 0) {
1731 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_op);
1732 } else if (use_color) {
1733 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1734 } else if (item->prefs->color != 0) {
1735 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1736 gtk_cmctree_node_set_foreground(ctree, node, &gdk_color);
1738 } else if (use_color) {
1739 gtk_cmctree_node_set_foreground(ctree, node, &folderview->color_new);
1740 } else if (item->prefs->color != 0) {
1741 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1742 gtk_cmctree_node_set_foreground(ctree, node, &gdk_color);
1745 gtk_cmctree_node_set_row_style(ctree, node, style);
1747 prev_style = gtk_cmctree_node_get_row_style(ctree, node);
1749 GtkStyle *ctree_style = gtk_widget_get_style(GTK_WIDGET(ctree));
1751 style = gtk_style_copy(prev_style);
1752 style->text[GTK_STATE_NORMAL] = ctree_style->text[GTK_STATE_NORMAL];
1753 style->text[GTK_STATE_SELECTED] = ctree_style->text[GTK_STATE_SELECTED];
1754 gtk_cmctree_node_set_row_style(ctree, node, style);
1755 g_object_unref(style);
1758 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1759 folderview_update_node(folderview, node);
1762 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1765 FolderView *folderview;
1767 GtkCMCTreeNode *node;
1769 cm_return_if_fail(item != NULL);
1771 for (list = folderview_list; list != NULL; list = list->next) {
1772 folderview = (FolderView *)list->data;
1773 ctree = GTK_CMCTREE(folderview->ctree);
1775 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1776 if (node && item->search_match != matches) {
1777 item->search_match = matches;
1778 folderview_update_node(folderview, node);
1783 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1785 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1786 FolderView *folderview = (FolderView *)data;
1788 GtkCMCTreeNode *node;
1789 cm_return_val_if_fail(update_info != NULL, TRUE);
1790 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1791 cm_return_val_if_fail(folderview != NULL, FALSE);
1793 ctree = GTK_CMCTREE(folderview->ctree);
1795 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1798 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1799 folderview_update_node(folderview, node);
1801 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1802 update_info->item == folderview->summaryview->folder_item &&
1803 update_info->item != NULL)
1804 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1805 summary_show(folderview->summaryview, update_info->item);
1811 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1812 GNode *gnode, GtkCMCTreeNode *cnode,
1815 FolderView *folderview = (FolderView *)data;
1816 FolderItem *item = FOLDER_ITEM(gnode->data);
1818 cm_return_val_if_fail(item != NULL, FALSE);
1820 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1821 folderview_update_node(folderview, cnode);
1826 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1829 FolderView *folderview = (FolderView *)data;
1832 if (GTK_CMCTREE_ROW(node)->children) {
1833 item = gtk_cmctree_node_get_row_data(ctree, node);
1834 cm_return_if_fail(item != NULL);
1836 if (!item->collapsed)
1837 gtk_cmctree_expand(ctree, node);
1839 folderview_update_node(folderview, node);
1843 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1844 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1847 GtkCMCTreeNode *node, *parent, *sibling;
1849 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1851 g_warning("%s not found.", item->path);
1853 parent = GTK_CMCTREE_ROW(node)->parent;
1854 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1855 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1857 sibling = GTK_CMCTREE_ROW(parent)->children;
1861 tmp = gtk_cmctree_node_get_row_data
1863 if (tmp && tmp->stype != F_NORMAL)
1864 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1868 if (node != sibling)
1869 gtk_cmctree_move(ctree, node, parent, sibling);
1876 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1879 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1880 GtkCMCTreeNode *prev = NULL;
1882 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1883 gtk_sctree_sort_recursive(ctree, root);
1884 if (root && GTK_CMCTREE_ROW(root)->parent) {
1885 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1888 set_special_folder(ctree, folder->inbox, root, &prev);
1889 set_special_folder(ctree, folder->outbox, root, &prev);
1890 set_special_folder(ctree, folder->draft, root, &prev);
1891 set_special_folder(ctree, folder->queue, root, &prev);
1892 set_special_folder(ctree, folder->trash, root, &prev);
1893 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1896 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1898 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1899 GtkCMCTreeNode *root;
1901 cm_return_if_fail(folder != NULL);
1903 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1904 folderview_gnode_func, folderview);
1905 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1907 folderview_sort_folders(folderview, root, folder);
1910 /* callback functions */
1911 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1912 GdkEventButton *event)
1916 FolderViewPopup *fpopup;
1917 GtkActionGroup *action_group;
1919 FolderItem *special_trash = NULL, *special_queue = NULL;
1921 GtkUIManager *ui_manager = gtk_ui_manager_new();
1923 if (folderview->ui_manager)
1924 g_object_unref(folderview->ui_manager);
1926 folderview->ui_manager = ui_manager;
1927 item = folderview_get_selected_item(folderview);
1929 cm_return_if_fail(item != NULL);
1930 cm_return_if_fail(item->folder != NULL);
1931 folder = item->folder;
1933 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1936 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1938 fpopup = g_hash_table_lookup(folderview_popups, "common");
1939 action_group = g_hash_table_lookup(folderview->popups, "common");
1942 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1943 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1944 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1946 if (fpopup->add_menuitems)
1947 fpopup->add_menuitems(ui_manager, item);
1949 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1950 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1959 if (fpopup->set_sensitivity != NULL)
1960 fpopup->set_sensitivity(ui_manager, item);
1962 if (NULL != (ac = account_find_from_item(item))) {
1963 special_trash = account_get_special_folder(ac, F_TRASH);
1964 special_queue = account_get_special_folder(ac, F_QUEUE);
1967 if ((item == folder->trash || item == special_trash
1968 || folder_has_parent_of_type(item, F_TRASH))) {
1969 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1970 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1973 if ((item == folder->queue || item == special_queue
1974 || folder_has_parent_of_type(item, F_QUEUE))) {
1975 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1979 #define SET_SENS(name, sens) \
1980 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1982 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1983 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1984 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1985 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1986 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1987 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1988 folderview->selected == folderview->opened);
1989 SET_SENS("FolderViewPopup/Properties", TRUE);
1991 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1992 item->total_msgs >= 1 && !item->processing_pending);
1993 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1994 !item->no_select && !item->processing_pending);
1996 if (item == folder->trash || item == special_trash
1997 || folder_has_parent_of_type(item, F_TRASH)) {
1998 GSList *msglist = folder_item_get_msg_list(item);
1999 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2000 procmsg_msg_list_free(msglist);
2002 if (item == folder->queue || item == special_queue
2003 || folder_has_parent_of_type(item, F_QUEUE)) {
2004 GSList *msglist = folder_item_get_msg_list(item);
2005 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2006 procmsg_msg_list_free(msglist);
2010 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2011 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2012 g_signal_connect(G_OBJECT(popup), "selection_done",
2013 G_CALLBACK(folderview_popup_close),
2015 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2016 event->button, event->time);
2019 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2020 FolderView *folderview)
2022 GtkCMCList *clist = GTK_CMCLIST(ctree);
2023 gint prev_row = -1, row = -1, column = -1;
2025 if (!event) return FALSE;
2026 if (event->window != clist->clist_window) return FALSE;
2028 if (event->button == 1 || event->button == 2) {
2029 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2030 folderview->open_folder = TRUE;
2032 if (event->type == GDK_2BUTTON_PRESS) {
2033 if (clist->selection) {
2034 GtkCMCTreeNode *node;
2036 node = GTK_CMCTREE_NODE(clist->selection->data);
2038 gtk_cmctree_toggle_expansion(
2041 folderview->open_folder = FALSE;
2048 if (event->button == 2 || event->button == 3) {
2050 if (clist->selection) {
2051 GtkCMCTreeNode *node;
2053 node = GTK_CMCTREE_NODE(clist->selection->data);
2055 prev_row = gtkut_ctree_get_nth_from_node
2056 (GTK_CMCTREE(ctree), node);
2059 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2062 if (prev_row != row) {
2063 gtk_cmclist_unselect_all(clist);
2064 if (event->button == 2)
2065 folderview_select_node
2067 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2070 gtk_cmclist_select_row(clist, row, column);
2074 if (event->button != 3) return FALSE;
2076 folderview_set_sens_and_popup_menu(folderview, row, event);
2080 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2081 FolderView *folderview)
2083 int row = -1, column = -1;
2085 if (!event) return FALSE;
2087 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2090 if (event->button == 1 && folderview->open_folder == FALSE &&
2091 folderview->opened != NULL) {
2092 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2093 folderview->opened);
2094 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2100 #define BREAK_ON_MODIFIER_KEY() \
2101 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2103 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2104 FolderView *folderview)
2106 GtkCMCTreeNode *node;
2109 if (!event) return FALSE;
2111 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2114 switch (event->keyval) {
2116 if (folderview->selected) {
2117 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2118 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2119 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2120 folderview->selected);
2122 folderview_select_node(folderview,
2123 folderview->selected);
2127 case GDK_KEY_Return:
2128 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2129 gtk_cmctree_toggle_expansion(
2130 GTK_CMCTREE(folderview->ctree),
2131 folderview->selected);
2135 case GDK_KEY_Return:
2136 case GDK_KEY_KP_Enter:
2137 if (folderview->selected)
2138 folderview_select_node(folderview, folderview->selected);
2142 BREAK_ON_MODIFIER_KEY();
2143 if (folderview->selected) {
2144 if (folderview->opened == folderview->selected &&
2145 (!folderview->summaryview->folder_item ||
2146 folderview->summaryview->folder_item->total_msgs == 0))
2147 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2149 folderview_select_node(folderview,
2150 folderview->selected);
2154 if (folderview->selected) {
2155 /* If the folder is expanded and can be collapsed, do that... */
2156 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2157 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2158 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2159 folderview->selected);
2161 /* ...otherwise, move cursor to its parent node. */
2162 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2163 folderview->selected))) {
2164 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2165 NULL, folder_item_parent(item)))) {
2166 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2167 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2168 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2177 if (event->keyval == GDK_KEY_Home)
2178 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2180 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2181 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2183 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2185 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2186 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2196 typedef struct _PostponedSelectData
2199 GtkCMCTreeNode *row;
2201 FolderView *folderview;
2202 } PostponedSelectData;
2204 static gboolean postpone_select(void *data)
2206 PostponedSelectData *psdata = (PostponedSelectData *)data;
2207 debug_print("trying again\n");
2209 psdata->folderview->postpone_select_id = 0;
2210 psdata->folderview->open_folder = TRUE;
2211 main_window_cursor_normal(psdata->folderview->mainwin);
2212 STATUSBAR_POP(psdata->folderview->mainwin);
2213 folderview_selected(psdata->ctree, psdata->row,
2214 psdata->column, psdata->folderview);
2219 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2221 if (folderview->opened) {
2223 folderview->opened = NULL;
2227 FolderItem *olditem =
2228 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2229 folderview->opened);
2231 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2232 olditem->path ? olditem->path:olditem->name);
2233 /* will be null if we just moved the previously opened folder */
2234 STATUSBAR_PUSH(folderview->mainwin, buf);
2235 main_window_cursor_wait(folderview->mainwin);
2237 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2238 summary_show(folderview->summaryview, NULL);
2239 folder_item_close(olditem);
2240 main_window_cursor_normal(folderview->mainwin);
2241 STATUSBAR_POP(folderview->mainwin);
2242 if (olditem->folder->klass->item_closed)
2243 olditem->folder->klass->item_closed(olditem);
2248 if (folderview->opened &&
2249 !GTK_CMCTREE_ROW(folderview->opened)->children)
2250 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2252 folderview->opened = NULL;
2254 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2255 gint column, FolderView *folderview)
2257 static gboolean can_select = TRUE; /* exclusive lock */
2262 GtkCMCTreeNode *old_opened = folderview->opened;
2264 folderview->selected = row;
2266 debug_print("newly selected %p, opened %p\n", folderview->selected,
2267 folderview->opened);
2268 if (folderview->opened == row) {
2269 folderview->open_folder = FALSE;
2274 item = gtk_cmctree_node_get_row_data(ctree, row);
2277 folderview->open_folder = FALSE;
2281 if (!can_select || summary_is_locked(folderview->summaryview)) {
2282 if (folderview->opened) {
2283 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2284 gtk_cmctree_select(ctree, folderview->opened);
2286 folderview->open_folder = FALSE;
2291 if (!folderview->open_folder) {
2298 /* Save cache for old folder */
2299 /* We don't want to lose all caches if app crashes */
2300 /* Resets folderview->opened to NULL */
2301 folderview_close_opened(folderview, FALSE);
2303 /* CLAWS: set compose button type: news folder items
2304 * always have a news folder as parent */
2306 toolbar_set_compose_button
2307 (folderview->mainwin->toolbar,
2308 FOLDER_TYPE(item->folder) == F_NEWS ?
2309 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2312 debug_print("Folder %s is selected\n", item->path);
2314 if (!GTK_CMCTREE_ROW(row)->children)
2315 gtk_cmctree_expand(ctree, row);
2317 /* ungrab the mouse event */
2318 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2319 gtk_grab_remove(GTK_WIDGET(ctree));
2320 if (gdk_pointer_is_grabbed())
2321 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2325 /* TODO: wwp: avoid displaying (null) in the status bar */
2326 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2327 item->path : "(null)");
2328 debug_print("%s\n", buf);
2329 STATUSBAR_PUSH(folderview->mainwin, buf);
2332 main_window_cursor_wait(folderview->mainwin);
2334 if (folderview->scanning_folder == item->folder) {
2337 res = folder_item_open(item);
2340 if (res == -1 && item->no_select == FALSE) {
2341 main_window_cursor_normal(folderview->mainwin);
2342 STATUSBAR_POP(folderview->mainwin);
2344 alertpanel_error(_("Folder could not be opened."));
2346 folderview->open_folder = FALSE;
2350 } else if (res == -2 && item->no_select == FALSE) {
2351 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2352 data->ctree = ctree;
2354 data->column = column;
2355 data->folderview = folderview;
2356 debug_print("postponing open of %s till end of scan\n",
2357 item->path ? item->path:item->name);
2358 folderview->open_folder = FALSE;
2360 if (folderview->postpone_select_id != 0)
2361 g_source_remove(folderview->postpone_select_id);
2362 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2367 main_window_cursor_normal(folderview->mainwin);
2370 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2371 opened = summary_show(folderview->summaryview, item);
2373 folder_clean_cache_memory(item);
2376 gtkut_ctree_set_focus_row(ctree, old_opened);
2377 gtk_cmctree_select(ctree, old_opened);
2378 folderview->opened = old_opened;
2380 folderview->opened = row;
2381 if (gtk_cmctree_node_is_visible(ctree, row)
2382 != GTK_VISIBILITY_FULL)
2383 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2386 STATUSBAR_POP(folderview->mainwin);
2388 folderview->open_folder = FALSE;
2393 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2394 FolderView *folderview)
2398 item = gtk_cmctree_node_get_row_data(ctree, node);
2399 cm_return_if_fail(item != NULL);
2400 item->collapsed = FALSE;
2401 folderview_update_node(folderview, node);
2404 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2405 FolderView *folderview)
2409 item = gtk_cmctree_node_get_row_data(ctree, node);
2410 cm_return_if_fail(item != NULL);
2411 item->collapsed = TRUE;
2412 folderview_update_node(folderview, node);
2415 static void folderview_popup_close(GtkMenuShell *menu_shell,
2416 FolderView *folderview)
2418 if (!folderview->opened) return;
2420 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2423 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2424 FolderView *folderview)
2426 FolderColumnType type = folderview->col_state[column].type;
2428 prefs_common.folder_col_size[type] = width;
2431 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2433 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2434 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2435 GtkCMCTreeNode *node, *parent_node;
2436 gint *col_pos = folderview->col_pos;
2437 FolderItemUpdateData hookdata;
2439 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2440 if (parent_node == NULL)
2443 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2445 text[col_pos[F_COL_FOLDER]] = item->name;
2446 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2451 gtk_cmctree_expand(ctree, parent_node);
2452 gtk_cmctree_node_set_row_data(ctree, node, item);
2453 folderview_sort_folders(folderview, parent_node, item->folder);
2455 hookdata.item = item;
2456 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2457 hookdata.msg = NULL;
2458 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2460 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2463 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2465 FolderView *folderview = (FolderView *)data;
2467 GSList *mlist = NULL;
2469 FolderItem *special_trash = NULL;
2472 if (!folderview->selected) return;
2473 item = folderview_get_selected_item(folderview);
2474 cm_return_if_fail(item != NULL);
2475 cm_return_if_fail(item->folder != NULL);
2477 if (NULL != (ac = account_find_from_item(item)))
2478 special_trash = account_get_special_folder(ac, F_TRASH);
2480 if (item != item->folder->trash && item != special_trash
2481 && !folder_has_parent_of_type(item, F_TRASH)) return;
2483 if (prefs_common.ask_on_clean) {
2484 if (alertpanel(_("Empty trash"),
2485 _("Delete all messages in trash?"),
2486 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2487 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2491 mlist = folder_item_get_msg_list(item);
2493 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2494 MsgInfo * msginfo = (MsgInfo *) cur->data;
2495 if (MSG_IS_LOCKED(msginfo->flags))
2497 /* is it partially received? (partial_recv isn't cached) */
2498 if (msginfo->total_size != 0 &&
2499 msginfo->size != (off_t)msginfo->total_size)
2500 partial_mark_for_delete(msginfo);
2502 procmsg_msg_list_free(mlist);
2504 folder_item_remove_all_msg(item);
2507 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2509 FolderView *folderview = (FolderView *)data;
2511 FolderItem *special_queue = NULL;
2513 gchar *errstr = NULL;
2515 if (!folderview->selected) return;
2516 item = folderview_get_selected_item(folderview);
2517 cm_return_if_fail(item != NULL);
2518 cm_return_if_fail(item->folder != NULL);
2520 if (NULL != (ac = account_find_from_item(item)))
2521 special_queue = account_get_special_folder(ac, F_QUEUE);
2523 if (item != item->folder->queue && item != special_queue
2524 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2526 if (procmsg_queue_is_empty(item))
2529 if (prefs_common.work_offline)
2530 if (alertpanel(_("Offline warning"),
2531 _("You're working offline. Override?"),
2532 GTK_STOCK_NO, GTK_STOCK_YES,
2533 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2536 /* ask for confirmation before sending queued messages only
2537 in online mode and if there is at least one message queued
2538 in any of the folder queue
2540 if (prefs_common.confirm_send_queued_messages) {
2541 if (!prefs_common.work_offline) {
2542 if (alertpanel(_("Send queued messages"),
2543 _("Send all queued messages?"),
2544 GTK_STOCK_CANCEL, _("_Send"),
2545 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2550 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2552 alertpanel_error_log(_("Some errors occurred while "
2553 "sending queued messages."));
2555 alertpanel_error_log(_("Some errors occurred "
2556 "while sending queued messages:\n%s"), errstr);
2562 static void folderview_search_cb(GtkAction *action, gpointer data)
2564 FolderView *folderview = (FolderView *)data;
2565 summary_search(folderview->summaryview);
2568 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2570 FolderView *folderview = (FolderView *)data;
2573 if (!folderview->selected) return;
2575 item = folderview_get_selected_item(folderview);
2576 cm_return_if_fail(item != NULL);
2577 cm_return_if_fail(item->folder != NULL);
2579 item->processing_pending = TRUE;
2580 folder_item_apply_processing(item);
2581 item->processing_pending = FALSE;
2584 static void folderview_property_cb(GtkAction *action, gpointer data)
2586 FolderView *folderview = (FolderView *)data;
2589 if (!folderview->selected) return;
2591 item = folderview_get_selected_item(folderview);
2592 cm_return_if_fail(item != NULL);
2593 cm_return_if_fail(item->folder != NULL);
2595 prefs_folder_item_open(item);
2598 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2600 GSList *list = NULL;
2601 GSList *done = NULL;
2602 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2604 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2605 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2606 && list->data != node) {
2607 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2608 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2611 for (list = done; list != NULL; list = g_slist_next(list)) {
2612 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2618 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2619 FolderItem *to_folder, gboolean copy)
2621 FolderItem *new_folder = NULL;
2625 cm_return_if_fail(folderview != NULL);
2626 cm_return_if_fail(from_folder != NULL);
2627 cm_return_if_fail(to_folder != NULL);
2629 if (prefs_common.warn_dnd) {
2630 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2631 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2632 from_folder->name, to_folder->name);
2633 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2634 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2635 TRUE, NULL, ALERT_QUESTION);
2638 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2640 else if (status & G_ALERTDISABLE)
2641 prefs_common.warn_dnd = FALSE;
2644 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2645 from_folder->name, to_folder->name);
2646 STATUSBAR_PUSH(folderview->mainwin, buf);
2648 summary_clear_all(folderview->summaryview);
2649 folderview->opened = NULL;
2650 folderview->selected = NULL;
2651 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2653 main_window_cursor_wait(folderview->mainwin);
2655 statusbar_verbosity_set(FALSE);
2656 folder_item_update_freeze();
2657 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2658 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2659 statusbar_verbosity_set(FALSE);
2660 main_window_cursor_normal(folderview->mainwin);
2661 STATUSBAR_POP(folderview->mainwin);
2662 folder_item_update_thaw();
2663 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2665 folderview_sort_folders(folderview,
2666 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2667 NULL, to_folder), new_folder->folder);
2668 folderview_select(folderview, new_folder);
2669 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2671 statusbar_verbosity_set(FALSE);
2672 main_window_cursor_normal(folderview->mainwin);
2673 STATUSBAR_POP(folderview->mainwin);
2674 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2675 folder_item_update_thaw();
2677 case F_MOVE_FAILED_DEST_IS_PARENT:
2678 alertpanel_error(_("Source and destination are the same."));
2680 case F_MOVE_FAILED_DEST_IS_CHILD:
2681 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2682 _("Can't move a folder to one of its children."));
2684 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2685 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2688 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2693 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2696 static gint folderview_clist_compare(GtkCMCList *clist,
2697 gconstpointer ptr1, gconstpointer ptr2)
2699 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2700 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2702 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2704 return item1->order - item2->order;
2707 // if only one folder has an order it comes first
2708 if (item1->order > 0)
2712 if (item2->order > 0)
2718 return (item2->name != NULL);
2722 return g_utf8_collate(item1->name, item2->name);
2725 static void folderview_processing_cb(GtkAction *action, gpointer data)
2727 FolderView *folderview = (FolderView *)data;
2731 if (!folderview->selected) return;
2733 item = folderview_get_selected_item(folderview);
2734 cm_return_if_fail(item != NULL);
2735 cm_return_if_fail(item->folder != NULL);
2737 id = folder_item_get_identifier(item);
2738 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2741 prefs_filtering_open(&item->prefs->processing, title,
2742 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2746 void folderview_set_target_folder_color(gint color_op)
2749 FolderView *folderview;
2751 for (list = folderview_list; list != NULL; list = list->next) {
2752 folderview = (FolderView *)list->data;
2753 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2757 static gchar *last_smallfont = NULL;
2758 static gchar *last_normalfont = NULL;
2759 static gchar *last_boldfont = NULL;
2760 static gboolean last_derive = 0;
2762 void folderview_reinit_fonts(FolderView *folderview)
2765 g_free(last_smallfont);
2766 last_smallfont = NULL;
2767 g_free(last_normalfont);
2768 last_normalfont = NULL;
2769 g_free(last_boldfont);
2770 last_boldfont = NULL;
2773 void folderview_reflect_prefs(void)
2775 gboolean update_font = FALSE;
2776 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2777 FolderItem *item = folderview_get_selected_item(folderview);
2778 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2779 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2780 gint height = gtk_adjustment_get_value(pos);
2782 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW],
2783 &folderview->color_new);
2784 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_TGT_FOLDER],
2785 &folderview->color_op);
2787 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2788 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2789 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2790 last_derive != prefs_common.derive_from_normal_font)
2796 g_free(last_smallfont);
2797 last_smallfont = g_strdup(SMALL_FONT);
2798 g_free(last_normalfont);
2799 last_normalfont = g_strdup(NORMAL_FONT);
2800 g_free(last_boldfont);
2801 last_boldfont = g_strdup(BOLD_FONT);
2802 last_derive = prefs_common.derive_from_normal_font;
2804 #define STYLE_FREE(s) \
2806 g_object_unref(s); \
2810 STYLE_FREE(bold_style);
2814 folderview_set_fonts(folderview);
2816 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2817 folderview_column_set_titles(folderview);
2818 folderview_set_all();
2820 g_signal_handlers_block_by_func
2821 (G_OBJECT(folderview->ctree),
2822 G_CALLBACK(folderview_selected), folderview);
2825 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2826 GTK_CMCTREE(folderview->ctree), NULL, item);
2828 folderview_select(folderview, item);
2829 folderview->open_folder = FALSE;
2830 folderview->selected = node;
2833 g_signal_handlers_unblock_by_func
2834 (G_OBJECT(folderview->ctree),
2835 G_CALLBACK(folderview_selected), folderview);
2837 pos = gtk_scrolled_window_get_vadjustment(
2838 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2839 gtk_adjustment_set_value(pos, height);
2840 gtk_adjustment_changed(pos);
2841 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2844 static void drag_state_stop(FolderView *folderview)
2846 if (folderview->drag_timer_id)
2847 g_source_remove(folderview->drag_timer_id);
2848 folderview->drag_timer_id = 0;
2849 folderview->drag_node = NULL;
2852 static gboolean folderview_defer_expand(FolderView *folderview)
2854 if (folderview->drag_node) {
2855 folderview_recollapse_nodes(folderview, folderview->drag_node);
2856 if (folderview->drag_item->collapsed) {
2857 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2858 folderview->nodes_to_recollapse = g_slist_append
2859 (folderview->nodes_to_recollapse, folderview->drag_node);
2862 folderview->drag_item = NULL;
2863 folderview->drag_timer_id = 0;
2867 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2869 /* the idea is that we call drag_state_start() whenever we want expansion to
2870 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2871 * we need to call drag_state_stop() */
2872 drag_state_stop(folderview);
2873 /* request expansion */
2874 if (0 != (folderview->drag_timer_id = g_timeout_add
2875 (prefs_common.hover_timeout,
2876 (GSourceFunc)folderview_defer_expand,
2878 folderview->drag_node = node;
2879 folderview->drag_item = item;
2882 #ifndef GENERIC_UMPC
2883 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2884 FolderView *folderview)
2886 GdkDragContext *context;
2888 cm_return_if_fail(folderview != NULL);
2889 if (folderview->selected == NULL) return;
2890 if (folderview->nodes_to_recollapse)
2891 g_slist_free(folderview->nodes_to_recollapse);
2892 folderview->nodes_to_recollapse = NULL;
2893 context = gtk_drag_begin(widget, folderview->target_list,
2894 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2895 gtk_drag_set_icon_default(context);
2898 static void folderview_drag_data_get(GtkWidget *widget,
2899 GdkDragContext *drag_context,
2900 GtkSelectionData *selection_data,
2903 FolderView *folderview)
2907 gchar *source = NULL;
2908 if (info == TARGET_DUMMY) {
2909 sel = GTK_CMCLIST(folderview->ctree)->selection;
2913 item = gtk_cmctree_node_get_row_data
2914 (GTK_CMCTREE(folderview->ctree),
2915 GTK_CMCTREE_NODE(sel->data));
2917 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2918 gtk_selection_data_set(selection_data,
2919 gtk_selection_data_get_target(selection_data), 8,
2920 source, strlen(source));
2923 g_warning("unknown info %d", info);
2927 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2929 FolderUpdateData *hookdata;
2930 FolderView *folderview;
2934 folderview = (FolderView *) userdata;
2935 cm_return_val_if_fail(hookdata != NULL, FALSE);
2936 cm_return_val_if_fail(folderview != NULL, FALSE);
2938 ctree = folderview->ctree;
2939 cm_return_val_if_fail(ctree != NULL, FALSE);
2941 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2942 folderview_create_folder_node(folderview, hookdata->item);
2943 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2944 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2945 NULL, folder_item_parent(hookdata->item));
2946 folderview_sort_folders(folderview, node, hookdata->folder);
2947 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2948 GtkCMCTreeNode *node;
2950 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2952 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2953 if (folderview->selected == node)
2954 folderview->selected = NULL;
2955 if (folderview->opened == node)
2956 folderview->opened = NULL;
2958 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2959 /* do nothing, it's done by the ADD and REMOVE) */
2960 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2961 folderview_set(folderview);
2966 static gboolean folderview_dnd_scroll_cb(gpointer data)
2968 FolderView *folderview = (FolderView *)data;
2969 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2970 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2971 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2972 gint max = (int)gtk_adjustment_get_upper(pos) -
2973 (int)gtk_adjustment_get_page_size(pos);
2975 if (folderview->scroll_value == 0) {
2976 folderview->scroll_timeout_id = 0;
2980 if (folderview->scroll_value > 0 && new_val > max) {
2982 } else if (folderview->scroll_value < 0 && new_val < 0) {
2985 gtk_adjustment_set_value(pos, new_val);
2990 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2991 GdkDragContext *context,
2995 FolderView *folderview)
2998 FolderItem *item = NULL, *src_item = NULL;
2999 GtkCMCTreeNode *node = NULL;
3000 gboolean acceptable = FALSE;
3001 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3002 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3003 int height = (int)gtk_adjustment_get_page_size(pos);
3004 int total_height = (int)gtk_adjustment_get_upper(pos);
3005 int vpos = (int)gtk_adjustment_get_value(pos);
3006 int offset = prefs_common.show_col_headers ? 24:0;
3009 if (gtk_cmclist_get_selection_info
3010 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3011 GtkWidget *srcwidget;
3013 if (y > height - (48 - offset) && height + vpos < total_height) {
3014 dist = -(height - (48 - offset) - y);
3015 folderview->scroll_value = 1.41f * (1+(dist / 6));
3016 } else if (y < 72 - (24 - offset) && y >= 0) {
3017 dist = 72 - (24 - offset) - y;
3018 folderview->scroll_value = -1.41f * (1+(dist / 6));
3020 folderview->scroll_value = 0;
3022 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3023 folderview->scroll_timeout_id =
3024 g_timeout_add(30, folderview_dnd_scroll_cb,
3028 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3029 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3030 src_item = folderview->summaryview->folder_item;
3032 srcwidget = gtk_drag_get_source_widget(context);
3033 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3034 /* comes from summaryview */
3035 /* we are copying messages, so only accept folder items that are not
3036 the source item, are no root items and can copy messages */
3037 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3038 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3039 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3041 } else if (srcwidget == folderview->ctree) {
3042 /* comes from folderview */
3043 /* we are moving folder items, only accept folders that are not
3044 the source items and can copy messages and create folder items */
3045 if (item && item->folder && src_item && src_item != item &&
3046 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3047 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3048 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3049 || item->folder == src_item->folder))
3052 /* comes from another app */
3053 /* we are adding messages, so only accept folder items that are
3054 no root items and can copy messages */
3055 if (item && item->folder && folder_item_parent(item) != NULL
3056 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3057 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3062 if (acceptable || (src_item && src_item == item))
3063 drag_state_start(folderview, node, item);
3066 g_signal_handlers_block_by_func
3068 G_CALLBACK(folderview_selected), folderview);
3069 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3070 g_signal_handlers_unblock_by_func
3072 G_CALLBACK(folderview_selected), folderview);
3073 gdk_drag_status(context,
3074 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3075 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3077 if (folderview->opened)
3078 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3079 gdk_drag_status(context, 0, time);
3085 static void folderview_drag_leave_cb(GtkWidget *widget,
3086 GdkDragContext *context,
3088 FolderView *folderview)
3090 drag_state_stop(folderview);
3091 folderview->scroll_value = 0;
3092 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3095 static void free_info (gpointer stuff, gpointer data)
3100 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3101 guint time, FolderItem *item)
3104 GSList *msglist = NULL;
3105 list = uri_list_extract_filenames(data);
3106 if (!(item && item->folder && folder_item_parent(item) != NULL
3107 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3109 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3110 debug_print("item doesn't fit\n");
3114 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3115 debug_print("list is empty\n");
3118 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3119 MsgFileInfo *info = NULL;
3121 if (file_is_email((gchar *)tmp->data)) {
3122 info = g_new0(MsgFileInfo, 1);
3123 info->msginfo = NULL;
3124 info->file = (gchar *)tmp->data;
3125 msglist = g_slist_prepend(msglist, info);
3126 debug_print("file is a mail\n");
3128 debug_print("file isn't a mail\n");
3132 msglist = g_slist_reverse(msglist);
3133 folder_item_add_msgs(item, msglist, FALSE);
3134 g_slist_foreach(msglist, free_info, NULL);
3135 g_slist_free(msglist);
3136 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3138 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3140 list_free_strings_full(list);
3143 static void folderview_drag_received_cb(GtkWidget *widget,
3144 GdkDragContext *drag_context,
3147 GtkSelectionData *data,
3150 FolderView *folderview)
3153 FolderItem *item = NULL, *src_item;
3154 GtkCMCTreeNode *node;
3155 int offset = prefs_common.show_col_headers ? 24:0;
3157 folderview->scroll_value = 0;
3159 if (info == TARGET_DUMMY) {
3160 drag_state_stop(folderview);
3161 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3162 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3163 /* comes from summaryview */
3164 if (gtk_cmclist_get_selection_info
3165 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3168 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3169 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3170 src_item = folderview->summaryview->folder_item;
3172 if (item->no_select) {
3173 alertpanel_error(_("The destination folder can only be used to "
3174 "store subfolders."));
3177 /* re-check (due to acceptable possibly set for folder moves */
3178 if (!(item && item->folder && item->path && !item->no_select &&
3179 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3183 switch (gdk_drag_context_get_selected_action(drag_context)) {
3184 case GDK_ACTION_COPY:
3185 summary_copy_selected_to(folderview->summaryview, item);
3186 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3188 case GDK_ACTION_MOVE:
3189 case GDK_ACTION_DEFAULT:
3191 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3192 summary_copy_selected_to(folderview->summaryview, item);
3194 summary_move_selected_to(folderview->summaryview, item);
3195 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3198 /* comes from folderview */
3200 gboolean folder_is_normal = TRUE;
3201 gboolean copy = (GDK_ACTION_COPY ==
3202 gdk_drag_context_get_selected_action(drag_context));
3204 source = (char *)gtk_selection_data_get_data(data) + 17;
3205 if (gtk_cmclist_get_selection_info
3206 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3208 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3211 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3212 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3213 src_item = folder_find_item_from_identifier(source);
3217 src_item->stype == F_NORMAL &&
3218 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3219 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3220 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3221 !folder_has_parent_of_type(src_item, F_TRASH);
3222 if (!item || !src_item || !folder_is_normal) {
3223 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3227 folderview_move_folder(folderview, src_item, item, copy);
3228 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3230 folderview->nodes_to_recollapse = NULL;
3231 } else if (info == TARGET_MAIL_URI_LIST) {
3232 if (gtk_cmclist_get_selection_info
3233 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3236 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3238 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3239 debug_print("no node\n");
3242 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3244 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3245 debug_print("no item\n");
3248 folderview_finish_dnd(gtk_selection_data_get_data(data),
3249 drag_context, time, item);
3253 static void folderview_drag_end_cb(GtkWidget *widget,
3254 GdkDragContext *drag_context,
3255 FolderView *folderview)
3257 drag_state_stop(folderview);
3258 folderview->scroll_value = 0;
3259 g_slist_free(folderview->nodes_to_recollapse);
3260 folderview->nodes_to_recollapse = NULL;
3263 void folderview_register_popup(FolderViewPopup *fpopup)
3267 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3268 FolderView *folderview = folderviews->data;
3269 GtkActionGroup *factory;
3271 factory = create_action_group(folderview, fpopup);
3272 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3274 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3277 void folderview_unregister_popup(FolderViewPopup *fpopup)
3282 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3283 FolderView *folderview = folderviews->data;
3285 g_hash_table_remove(folderview->popups, fpopup->klass);
3287 g_hash_table_remove(folderview_popups, fpopup->klass);
3290 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3292 g_return_if_fail(folderview != NULL);
3293 g_return_if_fail(item != NULL);
3295 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3296 g_return_if_fail(ctree != NULL);
3298 GtkCMCTreeNode *node =
3299 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3300 g_return_if_fail(node != NULL);
3302 gtk_cmctree_remove_node(ctree, node);
3305 void folderview_freeze(FolderView *folderview)
3308 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3311 void folderview_thaw(FolderView *folderview)
3314 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3317 void folderview_grab_focus(FolderView *folderview)
3320 gtk_widget_grab_focus(folderview->ctree);
3323 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3326 prefs_folder_column_open();
3329 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3333 GdkEventButton *event = (GdkEventButton *)_event;
3334 FolderView *folderview = (FolderView *)user_data;
3336 cm_return_val_if_fail(folderview != NULL, FALSE);
3338 /* Only handle single button presses. */
3339 if (event->type == GDK_2BUTTON_PRESS ||
3340 event->type == GDK_3BUTTON_PRESS)
3343 /* Handle right-click for context menu */
3344 if (event->button == 3) {
3345 gtk_menu_popup(GTK_MENU(folderview->headerpopupmenu),
3346 NULL, NULL, NULL, NULL, 3, event->time);