2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2018 Hiroyuki Yamamoto and the Claws Mail team
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 *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *foldersubsxpm;
96 static GdkPixbuf *foldersubsopenxpm;
97 static GdkPixbuf *foldernoselectxpm;
98 static GdkPixbuf *foldernoselectopenxpm;
100 static GdkPixbuf *m_inboxxpm;
101 static GdkPixbuf *m_inboxhrmxpm;
102 static GdkPixbuf *m_inboxopenxpm;
103 static GdkPixbuf *m_inboxopenhrmxpm;
104 static GdkPixbuf *m_outboxxpm;
105 static GdkPixbuf *m_outboxhrmxpm;
106 static GdkPixbuf *m_outboxopenxpm;
107 static GdkPixbuf *m_outboxopenhrmxpm;
108 static GdkPixbuf *m_folderxpm;
109 static GdkPixbuf *m_folderhrmxpm;
110 static GdkPixbuf *m_folderopenxpm;
111 static GdkPixbuf *m_folderopenhrmxpm;
112 static GdkPixbuf *m_trashopenxpm;
113 static GdkPixbuf *m_trashopenhrmxpm;
114 static GdkPixbuf *m_trashxpm;
115 static GdkPixbuf *m_trashhrmxpm;
116 static GdkPixbuf *m_queuexpm;
117 static GdkPixbuf *m_queuehrmxpm;
118 static GdkPixbuf *m_queueopenxpm;
119 static GdkPixbuf *m_queueopenhrmxpm;
120 static GdkPixbuf *m_draftsxpm;
121 static GdkPixbuf *m_draftsopenxpm;
122 static GdkPixbuf *m_foldersubsxpm;
123 static GdkPixbuf *m_foldernoselectxpm;
125 static GdkPixbuf *newxpm;
126 static GdkPixbuf *unreadxpm;
127 static GdkPixbuf *readxpm;
129 static void folderview_select_node (FolderView *folderview,
130 GtkCMCTreeNode *node);
131 static void folderview_set_folders (FolderView *folderview);
132 static void folderview_sort_folders (FolderView *folderview,
133 GtkCMCTreeNode *root,
135 static void folderview_append_folder (FolderView *folderview,
137 static void folderview_update_node (FolderView *folderview,
138 GtkCMCTreeNode *node);
140 static gint folderview_clist_compare (GtkCMCList *clist,
144 /* callback functions */
145 static gboolean folderview_button_pressed (GtkWidget *ctree,
146 GdkEventButton *event,
147 FolderView *folderview);
148 static gboolean folderview_button_released (GtkWidget *ctree,
149 GdkEventButton *event,
150 FolderView *folderview);
151 static gboolean folderview_key_pressed (GtkWidget *widget,
153 FolderView *folderview);
154 static void folderview_selected (GtkCMCTree *ctree,
157 FolderView *folderview);
158 static void folderview_tree_expanded (GtkCMCTree *ctree,
159 GtkCMCTreeNode *node,
160 FolderView *folderview);
161 static void folderview_tree_collapsed (GtkCMCTree *ctree,
162 GtkCMCTreeNode *node,
163 FolderView *folderview);
164 static void folderview_popup_close (GtkMenuShell *menu_shell,
165 FolderView *folderview);
166 static void folderview_col_resized (GtkCMCList *clist,
169 FolderView *folderview);
171 static void mark_all_read_unread_handler (GtkAction *action,
176 static void mark_all_read_cb (GtkAction *action,
178 static void mark_all_unread_cb (GtkAction *action,
180 static void mark_all_read_recursive_cb (GtkAction *action,
182 static void mark_all_unread_recursive_cb (GtkAction *action,
185 static void folderview_empty_trash_cb (GtkAction *action,
188 static void folderview_send_queue_cb (GtkAction *action,
191 static void folderview_search_cb (GtkAction *action,
193 static void folderview_run_processing_cb(GtkAction *action,
196 static void folderview_property_cb (GtkAction *action,
199 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
200 GdkDragContext *context,
204 FolderView *folderview);
205 static void folderview_drag_leave_cb (GtkWidget *widget,
206 GdkDragContext *context,
208 FolderView *folderview);
209 static void folderview_drag_received_cb (GtkWidget *widget,
210 GdkDragContext *drag_context,
213 GtkSelectionData *data,
216 FolderView *folderview);
218 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
219 FolderView *folderview);
221 static void folderview_drag_data_get (GtkWidget *widget,
222 GdkDragContext *drag_context,
223 GtkSelectionData *selection_data,
226 FolderView *folderview);
227 static void folderview_drag_end_cb (GtkWidget *widget,
228 GdkDragContext *drag_context,
229 FolderView *folderview);
231 static void folderview_create_folder_node (FolderView *folderview,
233 static gboolean folderview_update_folder (gpointer source,
235 static gboolean folderview_update_item_claws (gpointer source,
237 static void folderview_processing_cb(GtkAction *action, gpointer data);
238 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
239 GdkEventButton *event);
240 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
242 static gboolean folderview_header_button_pressed(GtkWidget *widget,
246 GHashTable *folderview_popups;
248 static GtkActionEntry folderview_common_popup_entries[] =
250 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
251 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
252 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
253 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
254 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
255 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
256 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
257 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
258 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
259 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
260 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
261 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
265 static GtkActionEntry folderview_header_popup_entries[] =
267 {"FolderViewHeaderPopup", NULL, "FolderViewHeaderPopup", NULL, NULL, NULL },
268 {"FolderViewHeaderPopup/SetDisplayedColumns", NULL, N_("Set Displayed columns"), NULL, NULL, G_CALLBACK(folderview_header_set_displayed_columns_cb) }
271 GtkTargetEntry folderview_drag_types[] =
273 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
274 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
277 void folderview_initialize(void)
279 FolderViewPopup *fpopup;
281 fpopup = g_new0(FolderViewPopup, 1);
283 fpopup->klass = "common";
284 fpopup->path = "<CommonFolder>";
285 fpopup->entries = folderview_common_popup_entries;
286 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
287 fpopup->set_sensitivity = NULL;
289 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
290 g_hash_table_insert(folderview_popups, "common", fpopup);
293 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
295 FolderViewPopup *fpopup_common;
296 GtkActionGroup *action_group;
298 action_group = cm_menu_create_action_group(
300 fpopup->entries, fpopup->n_entries,
301 (gpointer)folderview);
303 if (fpopup->toggle_entries)
304 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
305 fpopup->n_toggle_entries,
306 (gpointer)folderview);
307 if (fpopup->radio_entries)
308 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
309 fpopup->n_radio_entries, fpopup->radio_default,
310 G_CALLBACK(fpopup->radio_callback),
311 (gpointer)folderview);
313 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
314 if (fpopup_common != fpopup) {
315 gtk_action_group_add_actions(action_group, fpopup_common->entries,
316 fpopup_common->n_entries,
317 (gpointer)folderview);
318 if (fpopup_common->toggle_entries)
319 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
320 fpopup_common->n_toggle_entries,
321 (gpointer)folderview);
322 if (fpopup_common->radio_entries)
323 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
324 fpopup_common->n_radio_entries, fpopup_common->radio_default,
325 G_CALLBACK(fpopup_common->radio_callback),
326 (gpointer)folderview);
332 static void create_action_groups(gpointer key, gpointer value, gpointer data)
334 FolderView *folderview = data;
335 FolderViewPopup *fpopup = value;
336 GtkActionGroup *group;
338 group = create_action_group(folderview, fpopup);
339 g_hash_table_insert(folderview->popups, fpopup->klass, group);
342 static void folderview_column_set_titles(FolderView *folderview)
344 GtkWidget *ctree = folderview->ctree;
345 GtkWidget *label_folder;
346 GtkWidget *label_new;
347 GtkWidget *label_unread;
348 GtkWidget *label_total;
349 GtkWidget *hbox_folder;
351 GtkWidget *hbox_unread;
352 GtkWidget *hbox_total;
353 gint *col_pos = folderview->col_pos;
355 debug_print("setting titles...\n");
356 gtk_widget_realize(folderview->ctree);
357 gtk_widget_show_all(folderview->scrolledwin);
359 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
360 * instead text (text overflows making them unreadable and ugly) */
361 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
362 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
363 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
365 label_folder = gtk_label_new(_("Folder"));
366 label_new = gtk_image_new_from_pixbuf(newxpm);
367 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
368 label_total = gtk_image_new_from_pixbuf(readxpm);
370 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
372 hbox_folder = gtk_hbox_new(FALSE, 4);
373 hbox_new = gtk_hbox_new(FALSE, 4);
374 hbox_unread = gtk_hbox_new(FALSE, 4);
375 hbox_total = gtk_hbox_new(FALSE, 4);
378 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
379 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
380 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
381 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
382 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
383 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
384 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
385 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
387 gtk_widget_show_all(hbox_folder);
388 gtk_widget_show_all(hbox_new);
389 gtk_widget_show_all(hbox_unread);
390 gtk_widget_show_all(hbox_total);
393 gtk_widget_set_size_request(hbox_new, -1, 20);
394 gtk_widget_set_size_request(hbox_unread, -1, 20);
395 gtk_widget_set_size_request(hbox_total, -1, 20);
398 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
399 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
400 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
401 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
407 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
408 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
409 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
412 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
414 FolderView *folderview = (FolderView *)data;
415 GdkEventButton event;
416 if (folderview_get_selected_item(folderview) == NULL)
420 event.time = gtk_get_current_event_time();
422 folderview_set_sens_and_popup_menu(folderview, -1,
429 static GtkWidget *folderview_ctree_create(FolderView *folderview)
433 FolderColumnState *col_state;
434 FolderColumnType type;
435 gchar *titles[N_FOLDER_COLS];
437 GtkWidget *scrolledwin = folderview->scrolledwin;
439 debug_print("creating tree...\n");
440 memset(titles, 0, sizeof(titles));
442 col_state = prefs_folder_column_get_config();
443 memset(titles, 0, sizeof(titles));
445 col_pos = folderview->col_pos;
447 for (i = 0; i < N_FOLDER_COLS; i++) {
448 folderview->col_state[i] = col_state[i];
449 type = col_state[i].type;
453 titles[col_pos[F_COL_FOLDER]] = _("Folder");
454 titles[col_pos[F_COL_NEW]] = _("New");
455 titles[col_pos[F_COL_UNREAD]] = _("Unread");
456 /* TRANSLATORS: This in Number sign in American style */
457 titles[col_pos[F_COL_TOTAL]] = _("#");
459 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
462 if (prefs_common.show_col_headers == FALSE)
463 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
466 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
467 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
469 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
470 col_pos[F_COL_UNREAD],
472 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
473 col_pos[F_COL_TOTAL],
475 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
476 GTK_CMCTREE_EXPANDER_TRIANGLE);
478 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
479 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
481 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
482 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
484 /* don't let title buttons take key focus */
485 for (i = 0; i < N_FOLDER_COLS; i++) {
486 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
487 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
488 prefs_common.folder_col_size[i]);
489 gtk_cmclist_set_column_visibility
490 (GTK_CMCLIST(ctree), i, col_state[i].visible);
492 g_signal_connect(G_OBJECT(GTK_CMCLIST(ctree)->column[i].button),
493 "button-press-event",
494 G_CALLBACK(folderview_header_button_pressed),
498 g_signal_connect(G_OBJECT(ctree), "key_press_event",
499 G_CALLBACK(folderview_key_pressed),
501 g_signal_connect(G_OBJECT(ctree), "button_press_event",
502 G_CALLBACK(folderview_button_pressed),
504 g_signal_connect(G_OBJECT(ctree), "popup-menu",
505 G_CALLBACK(folderview_popup_menu), folderview);
506 g_signal_connect(G_OBJECT(ctree), "button_release_event",
507 G_CALLBACK(folderview_button_released),
509 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
510 G_CALLBACK(folderview_selected), folderview);
512 /* drag-n-dropping folders on maemo is impractical as this
513 * opens the folder almost everytime */
514 g_signal_connect(G_OBJECT(ctree), "start_drag",
515 G_CALLBACK(folderview_start_drag), folderview);
517 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
518 G_CALLBACK(folderview_drag_data_get),
521 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
522 G_CALLBACK(folderview_tree_expanded),
524 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
525 G_CALLBACK(folderview_tree_collapsed),
528 g_signal_connect(G_OBJECT(ctree), "resize_column",
529 G_CALLBACK(folderview_col_resized),
533 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
534 folderview_drag_types, 2,
535 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
536 g_signal_connect(G_OBJECT(ctree), "drag_motion",
537 G_CALLBACK(folderview_drag_motion_cb),
539 g_signal_connect(G_OBJECT(ctree), "drag_leave",
540 G_CALLBACK(folderview_drag_leave_cb),
542 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
543 G_CALLBACK(folderview_drag_received_cb),
545 g_signal_connect(G_OBJECT(ctree), "drag_end",
546 G_CALLBACK(folderview_drag_end_cb),
549 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
554 void folderview_set_column_order(FolderView *folderview)
556 GtkWidget *ctree = folderview->ctree;
557 FolderItem *item = folderview_get_selected_item(folderview);
558 FolderItem *sel_item = NULL, *op_item = NULL;
559 GtkWidget *scrolledwin = folderview->scrolledwin;
561 if (folderview->drag_timer_id != 0) {
562 g_source_remove(folderview->drag_timer_id);
563 folderview->drag_timer_id = 0;
565 if (folderview->deferred_refresh_id != 0) {
566 g_source_remove(folderview->deferred_refresh_id);
567 folderview->deferred_refresh_id = 0;
569 if (folderview->scroll_timeout_id != 0) {
570 g_source_remove(folderview->scroll_timeout_id);
571 folderview->scroll_timeout_id = 0;
573 if (folderview->postpone_select_id != 0) {
574 g_source_remove(folderview->postpone_select_id);
575 folderview->postpone_select_id = 0;
578 if (folderview->selected)
579 sel_item = folderview_get_selected_item(folderview);
580 if (folderview->opened)
581 op_item = folderview_get_opened_item(folderview);
583 debug_print("recreating tree...\n");
584 gtk_widget_destroy(folderview->ctree);
587 folderview->ctree = ctree = folderview_ctree_create(folderview);
588 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
589 GTK_CMCLIST(ctree)->hadjustment);
590 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
591 GTK_CMCLIST(ctree)->vadjustment);
592 gtk_widget_show(ctree);
595 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
597 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
599 folderview_set(folderview);
600 folderview_column_set_titles(folderview);
602 folderview_select(folderview,item);
605 FolderView *folderview_create(MainWindow *mainwin)
607 FolderView *folderview;
608 GtkWidget *scrolledwin;
611 debug_print("Creating folder view...\n");
612 folderview = g_new0(FolderView, 1);
614 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
615 gtk_scrolled_window_set_policy
616 (GTK_SCROLLED_WINDOW(scrolledwin),
617 GTK_POLICY_AUTOMATIC,
618 prefs_common.folderview_vscrollbar_policy);
619 gtk_widget_set_size_request(scrolledwin,
620 prefs_common.folderview_width,
621 prefs_common.folderview_height);
623 folderview->scrolledwin = scrolledwin;
624 ctree = folderview_ctree_create(folderview);
626 /* create popup factories */
627 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
628 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
630 gtk_action_group_add_actions(mainwin->action_group,
631 folderview_header_popup_entries,
632 G_N_ELEMENTS(folderview_header_popup_entries),
633 (gpointer)folderview);
635 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus", "FolderViewHeaderPopup", "FolderViewHeaderPopup", GTK_UI_MANAGER_MENU)
636 MENUITEM_ADDUI_MANAGER(mainwin->ui_manager, "/Menus/FolderViewHeaderPopup", "SetDisplayedColumns", "FolderViewHeaderPopup/SetDisplayedColumns", GTK_UI_MANAGER_MENUITEM)
638 folderview->headerpopupmenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
639 gtk_ui_manager_get_widget(mainwin->ui_manager,
640 "/Menus/FolderViewHeaderPopup") ));
642 folderview->ctree = ctree;
644 folderview->folder_update_callback_id =
645 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
646 folderview->folder_item_update_callback_id =
647 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
649 gtk_widget_show_all(scrolledwin);
651 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
652 folderview_list = g_list_append(folderview_list, folderview);
654 folderview->drag_timer_id = 0;
655 folderview->deferred_refresh_id = 0;
656 folderview->scroll_timeout_id = 0;
657 folderview->postpone_select_id = 0;
662 void folderview_init(FolderView *folderview)
664 GtkWidget *ctree = folderview->ctree;
666 PangoFontDescription *normal_font;
668 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
676 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
677 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
678 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
679 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
680 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
681 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
682 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
683 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
684 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
685 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
686 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
687 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
688 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
689 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
690 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
691 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
692 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
693 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
695 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
696 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
697 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
698 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
699 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
700 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
701 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
702 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
703 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
704 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
705 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
706 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
707 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
708 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
709 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
710 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
711 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
712 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
713 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
714 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
715 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
716 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
717 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
718 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
720 normal_font = pango_font_description_from_string(NORMAL_FONT);
722 gtk_widget_modify_font(ctree, normal_font);
723 pango_font_description_free(normal_font);
725 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
728 PangoFontDescription *font_desc;
729 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
730 font_desc = pango_font_description_from_string(NORMAL_FONT);
732 if (normal_style->font_desc)
733 pango_font_description_free
734 (normal_style->font_desc);
735 normal_style->font_desc = font_desc;
737 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
738 normal_color_style = gtk_style_copy(normal_style);
739 normal_color_style->text[GTK_STATE_NORMAL] = gdk_color;
743 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
744 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
745 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
746 PangoFontDescription *font_desc;
747 font_desc = pango_font_description_from_string(NORMAL_FONT);
749 pango_font_description_free(bold_style->font_desc);
750 bold_style->font_desc = font_desc;
752 pango_font_description_set_weight
753 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
755 PangoFontDescription *font_desc;
756 font_desc = pango_font_description_from_string(BOLD_FONT);
758 if (bold_style->font_desc)
759 pango_font_description_free
760 (bold_style->font_desc);
761 bold_style->font_desc = font_desc;
764 bold_color_style = gtk_style_copy(bold_style);
765 bold_color_style->text[GTK_STATE_NORMAL] = gdk_color;
767 bold_tgtfold_style = gtk_style_copy(bold_style);
768 bold_tgtfold_style->text[GTK_STATE_NORMAL] = folderview->color_op;
772 static gboolean folderview_defer_set(gpointer data)
774 FolderView *folderview = (FolderView *)data;
775 MainWindow *mainwin = folderview->mainwin;
779 if (mainwin->lock_count)
782 debug_print("doing deferred folderview_set now\n");
783 folderview_set(folderview);
785 folderview->deferred_refresh_id = 0;
789 void folderview_set(FolderView *folderview)
791 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
792 MainWindow *mainwin = folderview->mainwin;
793 FolderItem *sel_item = NULL, *op_item = NULL;
798 if (mainwin->lock_count) {
799 if (folderview->deferred_refresh_id == 0)
800 folderview->deferred_refresh_id =
801 g_timeout_add(500, folderview_defer_set, folderview);
802 debug_print("deferred folderview_set\n");
807 debug_print("Setting folder info...\n");
808 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
810 main_window_cursor_wait(mainwin);
812 if (folderview->selected)
813 sel_item = folderview_get_selected_item(folderview);
814 if (folderview->opened)
815 op_item = folderview_get_opened_item(folderview);
817 folderview->selected = NULL;
818 folderview->opened = NULL;
820 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
821 gtk_cmclist_clear(GTK_CMCLIST(ctree));
823 folderview_set_folders(folderview);
826 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
828 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
830 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
831 main_window_cursor_normal(mainwin);
832 STATUSBAR_POP(mainwin);
836 void folderview_set_all(void)
840 for (list = folderview_list; list != NULL; list = list->next)
841 folderview_set((FolderView *)list->data);
844 void folderview_select(FolderView *folderview, FolderItem *item)
846 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
847 GtkCMCTreeNode *node;
848 GtkCMCTreeNode *old_selected = folderview->selected;
852 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
853 if (node) folderview_select_node(folderview, node);
855 if (old_selected != node)
856 folder_update_op_count();
859 static void mark_all_read_cb(GtkAction *action, gpointer data)
861 mark_all_read_unread_handler(action, data, FALSE, TRUE);
864 static void mark_all_unread_cb(GtkAction *action, gpointer data)
866 mark_all_read_unread_handler(action, data, FALSE, FALSE);
869 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
871 mark_all_read_unread_handler(action, data, TRUE, TRUE);
874 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
876 mark_all_read_unread_handler(action, data, TRUE, FALSE);
879 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
880 gboolean recursive, gboolean read)
882 FolderView *folderview = (FolderView *)data;
888 item = folderview_get_selected_item(folderview);
893 title = _("Mark all as read");
894 message = recursive? _("Do you really want to mark all mails in this "
895 "folder and its sub-folders as read?") :
896 _("Do you really want to mark all mails in this "
899 title = _("Mark all as unread");
900 message = recursive? _("Do you really want to mark all mails in this "
901 "folder and its sub-folders as unread?") :
902 _("Do you really want to mark all mails in this "
903 "folder as unread?");
905 if (prefs_common.ask_mark_all_read) {
906 val = alertpanel_full(title, message,
907 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
908 TRUE, NULL, ALERT_QUESTION);
910 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
912 else if (val & G_ALERTDISABLE)
913 prefs_common.ask_mark_all_read = FALSE;
916 folder_item_update_freeze();
917 if (folderview->summaryview->folder_item != item && !recursive)
918 summary_lock(folderview->summaryview);
920 summary_freeze(folderview->summaryview);
924 folderutils_mark_all_read_recursive(item, TRUE);
926 folderutils_mark_all_read(item, TRUE);
929 folderutils_mark_all_read_recursive(item, FALSE);
931 folderutils_mark_all_read(item, FALSE);
933 if (folderview->summaryview->folder_item != item && !recursive)
934 summary_unlock(folderview->summaryview);
936 summary_thaw(folderview->summaryview);
937 folder_item_update_thaw();
940 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
942 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
944 cm_return_if_fail(node != NULL);
946 if (folderview->open_folder) {
950 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
951 gtkut_ctree_expand_parent_all(ctree, node);
953 folderview->open_folder = TRUE;
954 gtkut_ctree_set_focus_row(ctree, node);
955 gtk_cmctree_select(ctree, node);
956 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
957 if ((folderview->summaryview->folder_item &&
958 folderview->summaryview->folder_item->total_msgs > 0) ||
959 prefs_common.layout_mode == SMALL_LAYOUT)
960 summary_select_node(folderview->summaryview,
961 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
963 gtk_widget_grab_focus(folderview->ctree);
966 void folderview_unselect(FolderView *folderview)
968 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
970 (GTK_CMCTREE(folderview->ctree), folderview->opened);
972 folderview->selected = folderview->opened = NULL;
975 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
976 GtkCMCTreeNode *node,
982 node = gtkut_ctree_node_next(ctree, node);
984 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
986 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
987 item = gtk_cmctree_node_get_row_data(ctree, node);
990 if (item->stype == F_TRASH || item->stype == F_DRAFT)
994 if(item->unread_msgs > 0)
998 if(item->new_msgs > 0)
1002 if(item->marked_msgs > 0)
1006 if(item->total_msgs > 0)
1015 void folderview_select_next_with_flag(FolderView *folderview,
1018 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1019 GtkCMCTreeNode *node = NULL;
1020 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
1024 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1027 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1030 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1033 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1037 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1039 folderview_select_node(folderview, node);
1043 if (!folderview->opened ||
1044 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1048 /* search again from the first node */
1049 node = folderview_find_next_with_flag(ctree, NULL, flag);
1051 folderview_select_node(folderview, node);
1054 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1057 FolderItem *folderview_get_selected_item(FolderView *folderview)
1059 g_return_val_if_fail(folderview != NULL, NULL);
1060 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1062 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1064 if (!folderview->selected) return NULL;
1065 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1068 FolderItem *folderview_get_opened_item(FolderView *folderview)
1070 g_return_val_if_fail(folderview != NULL, NULL);
1071 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1073 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1075 if (!folderview->opened) return NULL;
1076 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1079 static void folderview_set_folders(FolderView *folderview)
1082 list = folder_get_list();
1084 for (; list != NULL; list = list->next) {
1085 folderview_append_folder(folderview, FOLDER(list->data));
1089 static gchar *get_scan_str(FolderItem *item)
1092 return g_strdup_printf(_("Scanning folder %s/%s..."),
1093 item->folder->name, item->path);
1095 return g_strdup_printf(_("Scanning folder %s..."),
1096 item->folder->name);
1098 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1102 for (list = folderview_list; list != NULL; list = list->next) {
1103 FolderView *folderview = (FolderView *)list->data;
1104 MainWindow *mainwin = folderview->mainwin;
1105 gchar *str = get_scan_str(item);
1107 STATUSBAR_PUSH(mainwin, str);
1108 STATUSBAR_POP(mainwin);
1113 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1116 MainWindow *mainwin = mainwindow_get_mainwindow();
1117 FolderView *folderview = NULL;
1118 GtkAdjustment *pos = NULL;
1121 cm_return_if_fail(folder != NULL);
1123 if (!folder->klass->scan_tree) return;
1126 alertpanel_full(_("Rebuild folder tree"),
1127 _("Rebuilding the folder tree will remove "
1128 "local caches. Do you want to continue?"),
1129 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
1130 FALSE, NULL, ALERT_WARNING)
1131 != G_ALERTALTERNATE) {
1137 window = label_window_create(_("Rebuilding folder tree..."));
1139 window = label_window_create(_("Scanning folder tree..."));
1142 folderview = mainwin->folderview;
1145 pos = gtk_scrolled_window_get_vadjustment(
1146 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1147 height = gtk_adjustment_get_value(pos);
1150 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1151 folder_scan_tree(folder, rebuild);
1152 folder_set_ui_func(folder, NULL, NULL);
1154 folderview_set_all();
1157 pos = gtk_scrolled_window_get_vadjustment(
1158 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1159 gtk_adjustment_set_value(pos, height);
1160 gtk_adjustment_changed(pos);
1162 label_window_destroy(window);
1166 /** folderview_check_new()
1167 * Scan and update the folder and return the
1168 * count the number of new messages since last check.
1169 * \param folder the folder to check for new messages
1170 * \return the number of new messages since last check
1172 gint folderview_check_new(Folder *folder)
1176 FolderView *folderview;
1178 GtkCMCTreeNode *node;
1180 gint former_new_msgs = 0;
1181 gint former_new = 0, former_unread = 0, former_total;
1183 for (list = folderview_list; list != NULL; list = list->next) {
1184 folderview = (FolderView *)list->data;
1185 ctree = GTK_CMCTREE(folderview->ctree);
1186 folderview->scanning_folder = folder;
1188 main_window_lock(folderview->mainwin);
1190 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1191 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1193 item = gtk_cmctree_node_get_row_data(ctree, node);
1194 if (!item || !item->path || !item->folder) continue;
1195 if (item->no_select) continue;
1196 if (folder && folder != item->folder) continue;
1197 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1198 if (!item->prefs->newmailcheck) continue;
1199 if (item->processing_pending == TRUE) {
1200 debug_print("skipping %s, processing pending\n",
1201 item->path ? item->path : item->name);
1204 if (item->scanning != ITEM_NOT_SCANNING) {
1205 debug_print("skipping %s, scanning\n",
1206 item->path ? item->path : item->name);
1210 str = get_scan_str(item);
1212 STATUSBAR_PUSH(folderview->mainwin, str);
1216 folderview_scan_tree_func(item->folder, item, NULL);
1217 former_new = item->new_msgs;
1218 former_unread = item->unread_msgs;
1219 former_total = item->total_msgs;
1221 if (item->folder->klass->scan_required &&
1222 (item->folder->klass->scan_required(item->folder, item) ||
1223 item->folder->inbox == item ||
1224 item->opened == TRUE ||
1225 item->processing_pending == TRUE)) {
1226 if (folder_item_scan(item) < 0) {
1228 summaryview_unlock(folderview->summaryview, item);
1229 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1230 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1231 item->path ? item->path:item->name);
1232 STATUSBAR_POP(folderview->mainwin);
1234 } else if (!FOLDER_IS_LOCAL(folder)) {
1235 STATUSBAR_POP(folderview->mainwin);
1240 } else if (!item->folder->klass->scan_required) {
1241 if (folder_item_scan(item) < 0) {
1242 summaryview_unlock(folderview->summaryview, item);
1243 if (folder && !FOLDER_IS_LOCAL(folder)) {
1244 STATUSBAR_POP(folderview->mainwin);
1249 if (former_new != item->new_msgs ||
1250 former_unread != item->unread_msgs ||
1251 former_total != item->total_msgs)
1252 folderview_update_node(folderview, node);
1254 new_msgs += item->new_msgs;
1255 former_new_msgs += former_new;
1256 STATUSBAR_POP(folderview->mainwin);
1258 folderview->scanning_folder = NULL;
1259 main_window_unlock(folderview->mainwin);
1263 folder_write_list();
1264 /* Number of new messages since last check is the just the difference
1265 * between former_new_msgs and new_msgs. If new_msgs is less than
1266 * former_new_msgs, that would mean another session accessed the folder
1267 * and the result is not well defined.
1269 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1273 void folderview_check_new_all(void)
1277 FolderView *folderview;
1279 folderview = (FolderView *)folderview_list->data;
1282 main_window_lock(folderview->mainwin);
1283 window = label_window_create
1284 (_("Checking for new messages in all folders..."));
1286 list = folder_get_list();
1287 for (; list != NULL; list = list->next) {
1288 Folder *folder = list->data;
1290 folderview_check_new(folder);
1293 folder_write_list();
1294 folderview_set_all();
1296 label_window_destroy(window);
1297 main_window_unlock(folderview->mainwin);
1301 static gboolean folderview_have_children_sub(FolderView *folderview,
1307 if (!item || !item->folder || !item->folder->node)
1310 node = item->folder->node;
1312 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1313 node = node->children;
1315 if (in_sub && item->total_msgs > 0) {
1319 while (node != NULL) {
1320 if (node && node->data) {
1321 FolderItem *next_item = (FolderItem*) node->data;
1323 if (folderview_have_children_sub(folderview,
1332 static gboolean folderview_have_children(FolderView *folderview,
1335 return folderview_have_children_sub(folderview, item, FALSE);
1338 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1344 if (!item || !item->folder || !item->folder->node)
1347 node = item->folder->node;
1349 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1350 node = node->children;
1353 (item->new_msgs > 0 ||
1354 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1358 while (node != NULL) {
1359 if (node && node->data) {
1360 FolderItem *next_item = (FolderItem*) node->data;
1362 if (folderview_have_new_children_sub(folderview,
1371 static gboolean folderview_have_new_children(FolderView *folderview,
1374 return folderview_have_new_children_sub(folderview, item, FALSE);
1377 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1383 if (!item || !item->folder || !item->folder->node)
1386 node = item->folder->node;
1388 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1389 node = node->children;
1392 (item->unread_msgs > 0 ||
1393 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1397 while (node != NULL) {
1398 if (node && node->data) {
1399 FolderItem *next_item = (FolderItem*) node->data;
1401 if (folderview_have_unread_children_sub(folderview,
1411 static gboolean folderview_have_unread_children(FolderView *folderview,
1414 return folderview_have_unread_children_sub(folderview, item, FALSE);
1417 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1423 if (!item || !item->folder || !item->folder->node) {
1427 node = item->folder->node;
1429 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1430 node = node->children;
1433 (((item->total_msgs > 0) &&
1434 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1438 while (node != NULL) {
1439 if (node && node->data) {
1440 FolderItem *next_item = (FolderItem*) node->data;
1442 if (folderview_have_read_children_sub(folderview,
1453 static gboolean folderview_have_read_children(FolderView *folderview,
1456 return folderview_have_read_children_sub(folderview, item, FALSE);
1459 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1465 if (!item || !item->folder || !item->folder->node)
1468 node = item->folder->node;
1470 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1471 node = node->children;
1473 if (in_sub && item->search_match){
1477 while (node != NULL) {
1478 if (node && node->data) {
1479 FolderItem *next_item = (FolderItem*) node->data;
1481 if (folderview_have_matching_children_sub(folderview,
1491 static gboolean folderview_have_matching_children(FolderView *folderview,
1494 return folderview_have_matching_children_sub(folderview, item, FALSE);
1497 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1503 if (!item || !item->folder || !item->folder->node)
1506 node = item->folder->node;
1508 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1509 node = node->children;
1511 if (item->marked_msgs != 0) {
1515 while (node != NULL) {
1516 if (node && node->data) {
1517 FolderItem *next_item = (FolderItem*) node->data;
1519 if (folderview_have_marked_children_sub(folderview,
1528 static gboolean folderview_have_marked_children(FolderView *folderview,
1531 return folderview_have_marked_children_sub(folderview, item, FALSE);
1534 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1536 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1537 GtkStyle *style = NULL;
1538 GtkStyle *color_style = NULL;
1540 GdkPixbuf *xpm, *openxpm;
1541 static GdkPixbuf *searchicon;
1542 gboolean mark = FALSE;
1545 gboolean add_unread_mark;
1546 gboolean add_sub_match_mark;
1547 gboolean use_bold, use_color;
1548 gint *col_pos = folderview->col_pos;
1549 SpecialFolderItemType stype;
1550 gboolean copiedstyle = FALSE;
1552 item = gtk_cmctree_node_get_row_data(ctree, node);
1553 cm_return_if_fail(item != NULL);
1555 if (!GTK_CMCTREE_ROW(node)->expanded)
1556 mark = folderview_have_marked_children(folderview, item);
1558 mark = (item->marked_msgs != 0);
1560 stype = item->stype;
1561 if (stype == F_NORMAL) {
1562 if (folder_has_parent_of_type(item, F_TRASH))
1564 else if (folder_has_parent_of_type(item, F_DRAFT))
1566 else if (folder_has_parent_of_type(item, F_OUTBOX))
1568 else if (folder_has_parent_of_type(item, F_QUEUE))
1573 if (item->hide_read_msgs || item->hide_read_threads) {
1574 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1575 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1577 xpm = mark?m_inboxxpm:inboxxpm;
1578 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1582 if (item->hide_read_msgs || item->hide_read_threads) {
1583 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1584 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1586 xpm = mark?m_outboxxpm:outboxxpm;
1587 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1591 if (item->hide_read_msgs || item->hide_read_threads) {
1592 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1593 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1595 xpm = mark?m_queuexpm:queuexpm;
1596 openxpm = mark?m_queueopenxpm:queueopenxpm;
1600 if (item->hide_read_msgs || item->hide_read_threads) {
1601 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1602 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1604 xpm = mark?m_trashxpm:trashxpm;
1605 openxpm = mark?m_trashopenxpm:trashopenxpm;
1609 xpm = mark?m_draftsxpm:draftsxpm;
1610 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1614 FOLDER_TYPE(item->folder) == F_IMAP &&
1615 item->folder->account->imap_subsonly) {
1616 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1617 openxpm = foldersubsopenxpm;
1618 } else if (item->no_select) {
1619 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1620 openxpm = foldernoselectopenxpm;
1621 } else if (item->hide_read_msgs || item->hide_read_threads) {
1622 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1623 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1625 xpm = mark?m_folderxpm:folderxpm;
1626 openxpm = mark?m_folderopenxpm:folderopenxpm;
1630 name = folder_item_get_name(item);
1632 if (!GTK_CMCTREE_ROW(node)->expanded) {
1633 add_unread_mark = folderview_have_unread_children(
1635 add_sub_match_mark = folderview_have_matching_children(
1638 add_unread_mark = FALSE;
1639 add_sub_match_mark = FALSE;
1642 if (item->search_match) {
1644 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1647 xpm = openxpm = searchicon;
1651 if (prefs_common.display_folder_unread) {
1652 if (folder_has_parent_of_type(item, F_QUEUE)) {
1653 /* only total_msgs matters here */
1654 if (item->total_msgs > 0) {
1655 /* show total number (should be equal to the unread number)
1657 str = g_strdup_printf("%s (%d%s%s)",
1658 name, item->total_msgs,
1659 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1660 (item->unreadmarked_msgs > 0) ? "!" : "");
1663 if (prefs_common.display_folder_unread == 1) {
1664 if (item->unread_msgs > 0) {
1665 /* show unread number and signs */
1666 str = g_strdup_printf("%s (%d%s%s)",
1667 name, item->unread_msgs,
1668 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1669 (item->unreadmarked_msgs > 0) ? "!" : "");
1672 if (item->total_msgs > 0) {
1673 /* show unread number, total number and signs if any */
1674 str = g_strdup_printf("%s (%d/%d%s%s)",
1675 name, item->unread_msgs, item->total_msgs,
1676 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1677 (item->unreadmarked_msgs > 0) ? "!" : "");
1681 if ((str == NULL) &&
1682 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1683 /* no unread/total numbers, but at least one sign */
1684 str = g_strdup_printf("%s (%s%s)",
1686 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1687 (item->unreadmarked_msgs > 0) ? "!" : "");
1691 /* last fallback, folder name only or with +! sign */
1692 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1693 str = g_strdup_printf("%s%s",
1695 } else if (item->unreadmarked_msgs > 0) {
1696 str = g_strdup_printf("%s%s",
1698 } else if (add_sub_match_mark) {
1699 str = g_strdup_printf("%s%s",
1702 str = g_strdup_printf("%s", name);
1705 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1707 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1711 if (!folder_item_parent(item)) {
1712 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1713 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1714 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1716 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1717 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1718 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1721 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1722 folder_has_parent_of_type(item, F_TRASH)) {
1723 use_bold = use_color = FALSE;
1724 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1725 GSList *list = folder_item_get_msg_list(item);
1727 use_bold = use_color = FALSE;
1728 for (cur = list; cur; cur = cur->next) {
1729 MsgInfo *msginfo = (MsgInfo *)cur->data;
1730 if (!MSG_IS_DELETED(msginfo->flags)) {
1731 /* highlight queue folder if there are any messages */
1732 use_bold = use_color = TRUE;
1736 if (!GTK_CMCTREE_ROW(node)->expanded &&
1737 use_bold == FALSE &&
1738 folderview_have_children(folderview, item))
1739 use_bold = use_color = TRUE;
1740 procmsg_msg_list_free(list);
1742 /* if unread messages exist, print with bold font */
1743 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1745 /* if new messages exist, print with colored letter */
1747 (item->new_msgs > 0) ||
1749 folderview_have_new_children(folderview, item));
1752 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1757 if (item->prefs->color > 0 && !use_color) {
1758 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1759 color_style = gtk_style_copy(bold_style);
1761 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1762 style = color_style;
1763 } else if (use_color) {
1764 style = bold_color_style;
1767 if (item->op_count > 0) {
1768 style = bold_tgtfold_style;
1770 } else if (use_color) {
1771 style = normal_color_style;
1772 gtk_cmctree_node_set_foreground(ctree, node,
1773 &folderview->color_new);
1774 } else if (item->op_count > 0) {
1775 style = bold_tgtfold_style;
1776 } else if (item->prefs->color > 0) {
1778 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1779 color_style = gtk_style_copy(normal_style);
1781 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1782 style = color_style;
1784 style = normal_style;
1787 gtk_cmctree_node_set_row_style(ctree, node, style);
1789 g_object_unref(style);
1791 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1792 folderview_update_node(folderview, node);
1795 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1798 FolderView *folderview;
1800 GtkCMCTreeNode *node;
1802 cm_return_if_fail(item != NULL);
1804 for (list = folderview_list; list != NULL; list = list->next) {
1805 folderview = (FolderView *)list->data;
1806 ctree = GTK_CMCTREE(folderview->ctree);
1808 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1809 if (node && item->search_match != matches) {
1810 item->search_match = matches;
1811 folderview_update_node(folderview, node);
1816 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1818 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1819 FolderView *folderview = (FolderView *)data;
1821 GtkCMCTreeNode *node;
1822 cm_return_val_if_fail(update_info != NULL, TRUE);
1823 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1824 cm_return_val_if_fail(folderview != NULL, FALSE);
1826 ctree = GTK_CMCTREE(folderview->ctree);
1828 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1831 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1832 folderview_update_node(folderview, node);
1834 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1835 update_info->item == folderview->summaryview->folder_item &&
1836 update_info->item != NULL)
1837 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1838 summary_show(folderview->summaryview, update_info->item);
1844 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1845 GNode *gnode, GtkCMCTreeNode *cnode,
1848 FolderView *folderview = (FolderView *)data;
1849 FolderItem *item = FOLDER_ITEM(gnode->data);
1851 cm_return_val_if_fail(item != NULL, FALSE);
1853 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1854 folderview_update_node(folderview, cnode);
1859 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1862 FolderView *folderview = (FolderView *)data;
1865 if (GTK_CMCTREE_ROW(node)->children) {
1866 item = gtk_cmctree_node_get_row_data(ctree, node);
1867 cm_return_if_fail(item != NULL);
1869 if (!item->collapsed)
1870 gtk_cmctree_expand(ctree, node);
1872 folderview_update_node(folderview, node);
1876 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1877 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1880 GtkCMCTreeNode *node, *parent, *sibling;
1882 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1884 g_warning("%s not found.", item->path);
1886 parent = GTK_CMCTREE_ROW(node)->parent;
1887 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1888 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1890 sibling = GTK_CMCTREE_ROW(parent)->children;
1894 tmp = gtk_cmctree_node_get_row_data
1896 if (tmp && tmp->stype != F_NORMAL)
1897 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1901 if (node != sibling)
1902 gtk_cmctree_move(ctree, node, parent, sibling);
1909 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1912 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1913 GtkCMCTreeNode *prev = NULL;
1915 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1916 gtk_sctree_sort_recursive(ctree, root);
1917 if (root && GTK_CMCTREE_ROW(root)->parent) {
1918 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1921 set_special_folder(ctree, folder->inbox, root, &prev);
1922 set_special_folder(ctree, folder->outbox, root, &prev);
1923 set_special_folder(ctree, folder->draft, root, &prev);
1924 set_special_folder(ctree, folder->queue, root, &prev);
1925 set_special_folder(ctree, folder->trash, root, &prev);
1926 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1929 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1931 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1932 GtkCMCTreeNode *root;
1934 cm_return_if_fail(folder != NULL);
1936 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1937 folderview_gnode_func, folderview);
1938 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1940 folderview_sort_folders(folderview, root, folder);
1943 /* callback functions */
1944 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1945 GdkEventButton *event)
1949 FolderViewPopup *fpopup;
1950 GtkActionGroup *action_group;
1952 FolderItem *special_trash = NULL, *special_queue = NULL;
1954 GtkUIManager *ui_manager = gtk_ui_manager_new();
1956 if (folderview->ui_manager)
1957 g_object_unref(folderview->ui_manager);
1959 folderview->ui_manager = ui_manager;
1960 item = folderview_get_selected_item(folderview);
1962 cm_return_if_fail(item != NULL);
1963 cm_return_if_fail(item->folder != NULL);
1964 folder = item->folder;
1966 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1969 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1971 fpopup = g_hash_table_lookup(folderview_popups, "common");
1972 action_group = g_hash_table_lookup(folderview->popups, "common");
1975 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1979 if (fpopup->add_menuitems)
1980 fpopup->add_menuitems(ui_manager, item);
1982 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1983 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1984 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1985 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1986 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1987 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1988 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1989 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1990 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1992 if (fpopup->set_sensitivity != NULL)
1993 fpopup->set_sensitivity(ui_manager, item);
1995 if (NULL != (ac = account_find_from_item(item))) {
1996 special_trash = account_get_special_folder(ac, F_TRASH);
1997 special_queue = account_get_special_folder(ac, F_QUEUE);
2000 if ((item == folder->trash || item == special_trash
2001 || folder_has_parent_of_type(item, F_TRASH))) {
2002 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
2003 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
2006 if ((item == folder->queue || item == special_queue
2007 || folder_has_parent_of_type(item, F_QUEUE))) {
2008 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
2009 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
2012 #define SET_SENS(name, sens) \
2013 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
2015 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
2016 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
2017 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
2018 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
2019 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
2020 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
2021 folderview->selected == folderview->opened);
2022 SET_SENS("FolderViewPopup/Properties", TRUE);
2024 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
2025 item->total_msgs >= 1 && !item->processing_pending);
2026 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
2027 !item->no_select && !item->processing_pending);
2029 if (item == folder->trash || item == special_trash
2030 || folder_has_parent_of_type(item, F_TRASH)) {
2031 GSList *msglist = folder_item_get_msg_list(item);
2032 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2033 procmsg_msg_list_free(msglist);
2035 if (item == folder->queue || item == special_queue
2036 || folder_has_parent_of_type(item, F_QUEUE)) {
2037 GSList *msglist = folder_item_get_msg_list(item);
2038 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2039 procmsg_msg_list_free(msglist);
2043 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2044 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2045 g_signal_connect(G_OBJECT(popup), "selection_done",
2046 G_CALLBACK(folderview_popup_close),
2048 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2049 event->button, event->time);
2052 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2053 FolderView *folderview)
2055 GtkCMCList *clist = GTK_CMCLIST(ctree);
2056 gint prev_row = -1, row = -1, column = -1;
2058 if (!event) return FALSE;
2059 if (event->window != clist->clist_window) return FALSE;
2061 if (event->button == 1 || event->button == 2) {
2062 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2063 folderview->open_folder = TRUE;
2065 if (event->type == GDK_2BUTTON_PRESS) {
2066 if (clist->selection) {
2067 GtkCMCTreeNode *node;
2069 node = GTK_CMCTREE_NODE(clist->selection->data);
2071 gtk_cmctree_toggle_expansion(
2074 folderview->open_folder = FALSE;
2081 if (event->button == 2 || event->button == 3) {
2083 if (clist->selection) {
2084 GtkCMCTreeNode *node;
2086 node = GTK_CMCTREE_NODE(clist->selection->data);
2088 prev_row = gtkut_ctree_get_nth_from_node
2089 (GTK_CMCTREE(ctree), node);
2092 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2095 if (prev_row != row) {
2096 gtk_cmclist_unselect_all(clist);
2097 if (event->button == 2)
2098 folderview_select_node
2100 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2103 gtk_cmclist_select_row(clist, row, column);
2107 if (event->button != 3) return FALSE;
2109 folderview_set_sens_and_popup_menu(folderview, row, event);
2113 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2114 FolderView *folderview)
2116 int row = -1, column = -1;
2118 if (!event) return FALSE;
2120 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2123 if (event->button == 1 && folderview->open_folder == FALSE &&
2124 folderview->opened != NULL) {
2125 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2126 folderview->opened);
2127 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2133 #define BREAK_ON_MODIFIER_KEY() \
2134 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2136 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2137 FolderView *folderview)
2139 GtkCMCTreeNode *node;
2142 if (!event) return FALSE;
2144 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2147 switch (event->keyval) {
2149 if (folderview->selected) {
2150 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2151 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2152 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2153 folderview->selected);
2155 folderview_select_node(folderview,
2156 folderview->selected);
2160 case GDK_KEY_Return:
2161 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2162 gtk_cmctree_toggle_expansion(
2163 GTK_CMCTREE(folderview->ctree),
2164 folderview->selected);
2168 case GDK_KEY_Return:
2169 case GDK_KEY_KP_Enter:
2170 if (folderview->selected)
2171 folderview_select_node(folderview, folderview->selected);
2175 BREAK_ON_MODIFIER_KEY();
2176 if (folderview->selected) {
2177 if (folderview->opened == folderview->selected &&
2178 (!folderview->summaryview->folder_item ||
2179 folderview->summaryview->folder_item->total_msgs == 0))
2180 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2182 folderview_select_node(folderview,
2183 folderview->selected);
2187 if (folderview->selected) {
2188 /* If the folder is expanded and can be collapsed, do that... */
2189 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2190 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2191 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2192 folderview->selected);
2194 /* ...otherwise, move cursor to its parent node. */
2195 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2196 folderview->selected))) {
2197 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2198 NULL, folder_item_parent(item)))) {
2199 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2200 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2201 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2210 if (event->keyval == GDK_KEY_Home)
2211 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2213 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2214 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2216 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2218 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2219 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2229 typedef struct _PostponedSelectData
2232 GtkCMCTreeNode *row;
2234 FolderView *folderview;
2235 } PostponedSelectData;
2237 static gboolean postpone_select(void *data)
2239 PostponedSelectData *psdata = (PostponedSelectData *)data;
2240 debug_print("trying again\n");
2242 psdata->folderview->postpone_select_id = 0;
2243 psdata->folderview->open_folder = TRUE;
2244 main_window_cursor_normal(psdata->folderview->mainwin);
2245 STATUSBAR_POP(psdata->folderview->mainwin);
2246 folderview_selected(psdata->ctree, psdata->row,
2247 psdata->column, psdata->folderview);
2252 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2254 if (folderview->opened) {
2256 folderview->opened = NULL;
2260 FolderItem *olditem =
2261 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2262 folderview->opened);
2264 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2265 olditem->path ? olditem->path:olditem->name);
2266 /* will be null if we just moved the previously opened folder */
2267 STATUSBAR_PUSH(folderview->mainwin, buf);
2268 main_window_cursor_wait(folderview->mainwin);
2270 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2271 summary_show(folderview->summaryview, NULL);
2272 folder_item_close(olditem);
2273 main_window_cursor_normal(folderview->mainwin);
2274 STATUSBAR_POP(folderview->mainwin);
2275 if (olditem->folder->klass->item_closed)
2276 olditem->folder->klass->item_closed(olditem);
2281 if (folderview->opened &&
2282 !GTK_CMCTREE_ROW(folderview->opened)->children)
2283 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2285 folderview->opened = NULL;
2287 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2288 gint column, FolderView *folderview)
2290 static gboolean can_select = TRUE; /* exclusive lock */
2295 GtkCMCTreeNode *old_opened = folderview->opened;
2297 folderview->selected = row;
2299 debug_print("newly selected %p, opened %p\n", folderview->selected,
2300 folderview->opened);
2301 if (folderview->opened == row) {
2302 folderview->open_folder = FALSE;
2307 item = gtk_cmctree_node_get_row_data(ctree, row);
2310 folderview->open_folder = FALSE;
2314 if (!can_select || summary_is_locked(folderview->summaryview)) {
2315 if (folderview->opened) {
2316 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2317 gtk_cmctree_select(ctree, folderview->opened);
2319 folderview->open_folder = FALSE;
2324 if (!folderview->open_folder) {
2331 /* Save cache for old folder */
2332 /* We don't want to lose all caches if app crashes */
2333 /* Resets folderview->opened to NULL */
2334 folderview_close_opened(folderview, FALSE);
2336 /* CLAWS: set compose button type: news folder items
2337 * always have a news folder as parent */
2339 toolbar_set_compose_button
2340 (folderview->mainwin->toolbar,
2341 FOLDER_TYPE(item->folder) == F_NEWS ?
2342 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2345 debug_print("Folder %s is selected\n", item->path);
2347 if (!GTK_CMCTREE_ROW(row)->children)
2348 gtk_cmctree_expand(ctree, row);
2350 /* ungrab the mouse event */
2351 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2352 gtk_grab_remove(GTK_WIDGET(ctree));
2353 if (gdk_pointer_is_grabbed())
2354 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2358 /* TODO: wwp: avoid displaying (null) in the status bar */
2359 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2360 item->path : "(null)");
2361 debug_print("%s\n", buf);
2362 STATUSBAR_PUSH(folderview->mainwin, buf);
2365 main_window_cursor_wait(folderview->mainwin);
2367 if (folderview->scanning_folder == item->folder) {
2370 res = folder_item_open(item);
2373 if (res == -1 && item->no_select == FALSE) {
2374 main_window_cursor_normal(folderview->mainwin);
2375 STATUSBAR_POP(folderview->mainwin);
2377 alertpanel_error(_("Folder could not be opened."));
2379 folderview->open_folder = FALSE;
2383 } else if (res == -2 && item->no_select == FALSE) {
2384 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2385 data->ctree = ctree;
2387 data->column = column;
2388 data->folderview = folderview;
2389 debug_print("postponing open of %s till end of scan\n",
2390 item->path ? item->path:item->name);
2391 folderview->open_folder = FALSE;
2393 if (folderview->postpone_select_id != 0)
2394 g_source_remove(folderview->postpone_select_id);
2395 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2400 main_window_cursor_normal(folderview->mainwin);
2403 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2404 opened = summary_show(folderview->summaryview, item);
2406 folder_clean_cache_memory(item);
2409 gtkut_ctree_set_focus_row(ctree, old_opened);
2410 gtk_cmctree_select(ctree, old_opened);
2411 folderview->opened = old_opened;
2413 folderview->opened = row;
2414 if (gtk_cmctree_node_is_visible(ctree, row)
2415 != GTK_VISIBILITY_FULL)
2416 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2419 STATUSBAR_POP(folderview->mainwin);
2421 folderview->open_folder = FALSE;
2426 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2427 FolderView *folderview)
2431 item = gtk_cmctree_node_get_row_data(ctree, node);
2432 cm_return_if_fail(item != NULL);
2433 item->collapsed = FALSE;
2434 folderview_update_node(folderview, node);
2437 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2438 FolderView *folderview)
2442 item = gtk_cmctree_node_get_row_data(ctree, node);
2443 cm_return_if_fail(item != NULL);
2444 item->collapsed = TRUE;
2445 folderview_update_node(folderview, node);
2448 static void folderview_popup_close(GtkMenuShell *menu_shell,
2449 FolderView *folderview)
2451 if (!folderview->opened) return;
2453 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2456 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2457 FolderView *folderview)
2459 FolderColumnType type = folderview->col_state[column].type;
2461 prefs_common.folder_col_size[type] = width;
2464 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2466 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2467 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2468 GtkCMCTreeNode *node, *parent_node;
2469 gint *col_pos = folderview->col_pos;
2470 FolderItemUpdateData hookdata;
2472 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2473 if (parent_node == NULL)
2476 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2478 text[col_pos[F_COL_FOLDER]] = item->name;
2479 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2484 gtk_cmctree_expand(ctree, parent_node);
2485 gtk_cmctree_node_set_row_data(ctree, node, item);
2487 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2488 folderview_sort_folders(folderview, parent_node, item->folder);
2490 hookdata.item = item;
2491 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2492 hookdata.msg = NULL;
2493 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2495 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2498 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2500 FolderView *folderview = (FolderView *)data;
2502 GSList *mlist = NULL;
2504 FolderItem *special_trash = NULL;
2507 if (!folderview->selected) return;
2508 item = folderview_get_selected_item(folderview);
2509 cm_return_if_fail(item != NULL);
2510 cm_return_if_fail(item->folder != NULL);
2512 if (NULL != (ac = account_find_from_item(item)))
2513 special_trash = account_get_special_folder(ac, F_TRASH);
2515 if (item != item->folder->trash && item != special_trash
2516 && !folder_has_parent_of_type(item, F_TRASH)) return;
2518 if (prefs_common.ask_on_clean) {
2519 if (alertpanel(_("Empty trash"),
2520 _("Delete all messages in trash?"),
2521 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2522 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2526 mlist = folder_item_get_msg_list(item);
2528 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2529 MsgInfo * msginfo = (MsgInfo *) cur->data;
2530 if (MSG_IS_LOCKED(msginfo->flags))
2532 /* is it partially received? (partial_recv isn't cached) */
2533 if (msginfo->total_size != 0 &&
2534 msginfo->size != (off_t)msginfo->total_size)
2535 partial_mark_for_delete(msginfo);
2537 procmsg_msg_list_free(mlist);
2539 folder_item_remove_all_msg(item);
2542 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2544 FolderView *folderview = (FolderView *)data;
2546 FolderItem *special_queue = NULL;
2548 gchar *errstr = NULL;
2550 if (!folderview->selected) return;
2551 item = folderview_get_selected_item(folderview);
2552 cm_return_if_fail(item != NULL);
2553 cm_return_if_fail(item->folder != NULL);
2555 if (NULL != (ac = account_find_from_item(item)))
2556 special_queue = account_get_special_folder(ac, F_QUEUE);
2558 if (item != item->folder->queue && item != special_queue
2559 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2561 if (procmsg_queue_is_empty(item))
2564 if (prefs_common.work_offline)
2565 if (alertpanel(_("Offline warning"),
2566 _("You're working offline. Override?"),
2567 GTK_STOCK_NO, GTK_STOCK_YES,
2568 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2571 /* ask for confirmation before sending queued messages only
2572 in online mode and if there is at least one message queued
2573 in any of the folder queue
2575 if (prefs_common.confirm_send_queued_messages) {
2576 if (!prefs_common.work_offline) {
2577 if (alertpanel(_("Send queued messages"),
2578 _("Send all queued messages?"),
2579 GTK_STOCK_CANCEL, _("_Send"),
2580 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2585 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2587 alertpanel_error_log(_("Some errors occurred while "
2588 "sending queued messages."));
2590 alertpanel_error_log(_("Some errors occurred "
2591 "while sending queued messages:\n%s"), errstr);
2597 static void folderview_search_cb(GtkAction *action, gpointer data)
2599 FolderView *folderview = (FolderView *)data;
2600 summary_search(folderview->summaryview);
2603 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2605 FolderView *folderview = (FolderView *)data;
2608 if (!folderview->selected) return;
2610 item = folderview_get_selected_item(folderview);
2611 cm_return_if_fail(item != NULL);
2612 cm_return_if_fail(item->folder != NULL);
2614 item->processing_pending = TRUE;
2615 folder_item_apply_processing(item);
2616 item->processing_pending = FALSE;
2619 static void folderview_property_cb(GtkAction *action, gpointer data)
2621 FolderView *folderview = (FolderView *)data;
2624 if (!folderview->selected) return;
2626 item = folderview_get_selected_item(folderview);
2627 cm_return_if_fail(item != NULL);
2628 cm_return_if_fail(item->folder != NULL);
2630 prefs_folder_item_open(item);
2633 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2635 GSList *list = NULL;
2636 GSList *done = NULL;
2637 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2639 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2640 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2641 && list->data != node) {
2642 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2643 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2646 for (list = done; list != NULL; list = g_slist_next(list)) {
2647 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2653 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2654 FolderItem *to_folder, gboolean copy)
2656 FolderItem *new_folder = NULL;
2660 cm_return_if_fail(folderview != NULL);
2661 cm_return_if_fail(from_folder != NULL);
2662 cm_return_if_fail(to_folder != NULL);
2664 if (prefs_common.warn_dnd) {
2665 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2666 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2667 from_folder->name, to_folder->name);
2668 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2669 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2670 TRUE, NULL, ALERT_QUESTION);
2673 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2675 else if (status & G_ALERTDISABLE)
2676 prefs_common.warn_dnd = FALSE;
2679 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2680 from_folder->name, to_folder->name);
2681 STATUSBAR_PUSH(folderview->mainwin, buf);
2683 summary_clear_all(folderview->summaryview);
2684 folderview->opened = NULL;
2685 folderview->selected = NULL;
2686 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2688 main_window_cursor_wait(folderview->mainwin);
2690 statusbar_verbosity_set(FALSE);
2691 folder_item_update_freeze();
2692 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2693 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2694 statusbar_verbosity_set(FALSE);
2695 main_window_cursor_normal(folderview->mainwin);
2696 STATUSBAR_POP(folderview->mainwin);
2697 folder_item_update_thaw();
2698 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2700 folderview_sort_folders(folderview,
2701 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2702 NULL, to_folder), new_folder->folder);
2703 folderview_select(folderview, new_folder);
2704 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2706 statusbar_verbosity_set(FALSE);
2707 main_window_cursor_normal(folderview->mainwin);
2708 STATUSBAR_POP(folderview->mainwin);
2709 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2710 folder_item_update_thaw();
2712 case F_MOVE_FAILED_DEST_IS_PARENT:
2713 alertpanel_error(_("Source and destination are the same."));
2715 case F_MOVE_FAILED_DEST_IS_CHILD:
2716 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2717 _("Can't move a folder to one of its children."));
2719 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2720 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2723 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2728 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2731 static gint folderview_clist_compare(GtkCMCList *clist,
2732 gconstpointer ptr1, gconstpointer ptr2)
2734 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2735 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2737 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2739 return item1->order - item2->order;
2742 // if only one folder has an order it comes first
2743 if (item1->order > 0)
2747 if (item2->order > 0)
2753 return (item2->name != NULL);
2757 return g_utf8_collate(item1->name, item2->name);
2760 static void folderview_processing_cb(GtkAction *action, gpointer data)
2762 FolderView *folderview = (FolderView *)data;
2766 if (!folderview->selected) return;
2768 item = folderview_get_selected_item(folderview);
2769 cm_return_if_fail(item != NULL);
2770 cm_return_if_fail(item->folder != NULL);
2772 id = folder_item_get_identifier(item);
2773 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2776 prefs_filtering_open(&item->prefs->processing, title,
2777 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2781 void folderview_set_target_folder_color(gint color_op)
2785 FolderView *folderview;
2787 for (list = folderview_list; list != NULL; list = list->next) {
2788 folderview = (FolderView *)list->data;
2789 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2791 bold_tgtfold_style->text[GTK_STATE_NORMAL] =
2792 folderview->color_op;
2798 static gchar *last_smallfont = NULL;
2799 static gchar *last_normalfont = NULL;
2800 static gchar *last_boldfont = NULL;
2801 static gboolean last_derive = 0;
2803 void folderview_reinit_fonts(FolderView *folderview)
2806 g_free(last_smallfont);
2807 last_smallfont = NULL;
2808 g_free(last_normalfont);
2809 last_normalfont = NULL;
2810 g_free(last_boldfont);
2811 last_boldfont = NULL;
2814 void folderview_reflect_prefs(void)
2816 gboolean update_font = FALSE;
2817 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2818 FolderItem *item = folderview_get_selected_item(folderview);
2819 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2820 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2821 gint height = gtk_adjustment_get_value(pos);
2823 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2824 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2825 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2826 last_derive != prefs_common.derive_from_normal_font)
2832 g_free(last_smallfont);
2833 last_smallfont = g_strdup(SMALL_FONT);
2834 g_free(last_normalfont);
2835 last_normalfont = g_strdup(NORMAL_FONT);
2836 g_free(last_boldfont);
2837 last_boldfont = g_strdup(BOLD_FONT);
2838 last_derive = prefs_common.derive_from_normal_font;
2840 #define STYLE_FREE(s) \
2842 g_object_unref(s); \
2846 STYLE_FREE(normal_style);
2847 STYLE_FREE(normal_color_style);
2848 STYLE_FREE(bold_style);
2849 STYLE_FREE(bold_color_style);
2850 STYLE_FREE(bold_tgtfold_style);
2854 folderview_init(folderview);
2855 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2856 folderview_column_set_titles(folderview);
2857 folderview_set_all();
2859 g_signal_handlers_block_by_func
2860 (G_OBJECT(folderview->ctree),
2861 G_CALLBACK(folderview_selected), folderview);
2864 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2865 GTK_CMCTREE(folderview->ctree), NULL, item);
2867 folderview_select(folderview, item);
2868 folderview->open_folder = FALSE;
2869 folderview->selected = node;
2872 g_signal_handlers_unblock_by_func
2873 (G_OBJECT(folderview->ctree),
2874 G_CALLBACK(folderview_selected), folderview);
2876 pos = gtk_scrolled_window_get_vadjustment(
2877 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2878 gtk_adjustment_set_value(pos, height);
2879 gtk_adjustment_changed(pos);
2880 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2883 static void drag_state_stop(FolderView *folderview)
2885 if (folderview->drag_timer_id)
2886 g_source_remove(folderview->drag_timer_id);
2887 folderview->drag_timer_id = 0;
2888 folderview->drag_node = NULL;
2891 static gboolean folderview_defer_expand(FolderView *folderview)
2893 if (folderview->drag_node) {
2894 folderview_recollapse_nodes(folderview, folderview->drag_node);
2895 if (folderview->drag_item->collapsed) {
2896 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2897 folderview->nodes_to_recollapse = g_slist_append
2898 (folderview->nodes_to_recollapse, folderview->drag_node);
2901 folderview->drag_item = NULL;
2902 folderview->drag_timer_id = 0;
2906 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2908 /* the idea is that we call drag_state_start() whenever we want expansion to
2909 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2910 * we need to call drag_state_stop() */
2911 drag_state_stop(folderview);
2912 /* request expansion */
2913 if (0 != (folderview->drag_timer_id = g_timeout_add
2914 (prefs_common.hover_timeout,
2915 (GSourceFunc)folderview_defer_expand,
2917 folderview->drag_node = node;
2918 folderview->drag_item = item;
2921 #ifndef GENERIC_UMPC
2922 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2923 FolderView *folderview)
2925 GdkDragContext *context;
2927 cm_return_if_fail(folderview != NULL);
2928 if (folderview->selected == NULL) return;
2929 if (folderview->nodes_to_recollapse)
2930 g_slist_free(folderview->nodes_to_recollapse);
2931 folderview->nodes_to_recollapse = NULL;
2932 context = gtk_drag_begin(widget, folderview->target_list,
2933 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2934 gtk_drag_set_icon_default(context);
2937 static void folderview_drag_data_get(GtkWidget *widget,
2938 GdkDragContext *drag_context,
2939 GtkSelectionData *selection_data,
2942 FolderView *folderview)
2946 gchar *source = NULL;
2947 if (info == TARGET_DUMMY) {
2948 sel = GTK_CMCLIST(folderview->ctree)->selection;
2952 item = gtk_cmctree_node_get_row_data
2953 (GTK_CMCTREE(folderview->ctree),
2954 GTK_CMCTREE_NODE(sel->data));
2956 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2957 gtk_selection_data_set(selection_data,
2958 gtk_selection_data_get_target(selection_data), 8,
2959 source, strlen(source));
2962 g_warning("unknown info %d", info);
2966 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2968 FolderUpdateData *hookdata;
2969 FolderView *folderview;
2973 folderview = (FolderView *) userdata;
2974 cm_return_val_if_fail(hookdata != NULL, FALSE);
2975 cm_return_val_if_fail(folderview != NULL, FALSE);
2977 ctree = folderview->ctree;
2978 cm_return_val_if_fail(ctree != NULL, FALSE);
2980 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2981 folderview_create_folder_node(folderview, hookdata->item);
2982 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2983 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2984 NULL, folder_item_parent(hookdata->item));
2985 folderview_sort_folders(folderview, node, hookdata->folder);
2986 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2987 GtkCMCTreeNode *node;
2989 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2991 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2992 if (folderview->selected == node)
2993 folderview->selected = NULL;
2994 if (folderview->opened == node)
2995 folderview->opened = NULL;
2997 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2998 /* do nothing, it's done by the ADD and REMOVE) */
2999 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
3000 folderview_set(folderview);
3005 static gboolean folderview_dnd_scroll_cb(gpointer data)
3007 FolderView *folderview = (FolderView *)data;
3008 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3009 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3010 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
3011 gint max = (int)gtk_adjustment_get_upper(pos) -
3012 (int)gtk_adjustment_get_page_size(pos);
3014 if (folderview->scroll_value == 0) {
3015 folderview->scroll_timeout_id = 0;
3019 if (folderview->scroll_value > 0 && new_val > max) {
3021 } else if (folderview->scroll_value < 0 && new_val < 0) {
3024 gtk_adjustment_set_value(pos, new_val);
3029 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
3030 GdkDragContext *context,
3034 FolderView *folderview)
3037 FolderItem *item = NULL, *src_item = NULL;
3038 GtkCMCTreeNode *node = NULL;
3039 gboolean acceptable = FALSE;
3040 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3041 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3042 int height = (int)gtk_adjustment_get_page_size(pos);
3043 int total_height = (int)gtk_adjustment_get_upper(pos);
3044 int vpos = (int)gtk_adjustment_get_value(pos);
3045 int offset = prefs_common.show_col_headers ? 24:0;
3048 if (gtk_cmclist_get_selection_info
3049 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3050 GtkWidget *srcwidget;
3052 if (y > height - (48 - offset) && height + vpos < total_height) {
3053 dist = -(height - (48 - offset) - y);
3054 folderview->scroll_value = 1.41f * (1+(dist / 6));
3055 } else if (y < 72 - (24 - offset) && y >= 0) {
3056 dist = 72 - (24 - offset) - y;
3057 folderview->scroll_value = -1.41f * (1+(dist / 6));
3059 folderview->scroll_value = 0;
3061 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3062 folderview->scroll_timeout_id =
3063 g_timeout_add(30, folderview_dnd_scroll_cb,
3067 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3068 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3069 src_item = folderview->summaryview->folder_item;
3071 srcwidget = gtk_drag_get_source_widget(context);
3072 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3073 /* comes from summaryview */
3074 /* we are copying messages, so only accept folder items that are not
3075 the source item, are no root items and can copy messages */
3076 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3077 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3078 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3080 } else if (srcwidget == folderview->ctree) {
3081 /* comes from folderview */
3082 /* we are moving folder items, only accept folders that are not
3083 the source items and can copy messages and create folder items */
3084 if (item && item->folder && src_item && src_item != item &&
3085 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3086 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3087 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3088 || item->folder == src_item->folder))
3091 /* comes from another app */
3092 /* we are adding messages, so only accept folder items that are
3093 no root items and can copy messages */
3094 if (item && item->folder && folder_item_parent(item) != NULL
3095 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3096 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3101 if (acceptable || (src_item && src_item == item))
3102 drag_state_start(folderview, node, item);
3105 g_signal_handlers_block_by_func
3107 G_CALLBACK(folderview_selected), folderview);
3108 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3109 g_signal_handlers_unblock_by_func
3111 G_CALLBACK(folderview_selected), folderview);
3112 gdk_drag_status(context,
3113 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3114 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3116 if (folderview->opened)
3117 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3118 gdk_drag_status(context, 0, time);
3124 static void folderview_drag_leave_cb(GtkWidget *widget,
3125 GdkDragContext *context,
3127 FolderView *folderview)
3129 drag_state_stop(folderview);
3130 folderview->scroll_value = 0;
3131 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3134 static void free_info (gpointer stuff, gpointer data)
3139 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3140 guint time, FolderItem *item)
3143 GSList *msglist = NULL;
3144 list = uri_list_extract_filenames(data);
3145 if (!(item && item->folder && folder_item_parent(item) != NULL
3146 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3148 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3149 debug_print("item doesn't fit\n");
3153 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3154 debug_print("list is empty\n");
3157 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3158 MsgFileInfo *info = NULL;
3160 if (file_is_email((gchar *)tmp->data)) {
3161 info = g_new0(MsgFileInfo, 1);
3162 info->msginfo = NULL;
3163 info->file = (gchar *)tmp->data;
3164 msglist = g_slist_prepend(msglist, info);
3165 debug_print("file is a mail\n");
3167 debug_print("file isn't a mail\n");
3171 msglist = g_slist_reverse(msglist);
3172 folder_item_add_msgs(item, msglist, FALSE);
3173 g_slist_foreach(msglist, free_info, NULL);
3174 g_slist_free(msglist);
3175 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3177 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3179 list_free_strings_full(list);
3182 static void folderview_drag_received_cb(GtkWidget *widget,
3183 GdkDragContext *drag_context,
3186 GtkSelectionData *data,
3189 FolderView *folderview)
3192 FolderItem *item = NULL, *src_item;
3193 GtkCMCTreeNode *node;
3194 int offset = prefs_common.show_col_headers ? 24:0;
3196 folderview->scroll_value = 0;
3198 if (info == TARGET_DUMMY) {
3199 drag_state_stop(folderview);
3200 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3201 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3202 /* comes from summaryview */
3203 if (gtk_cmclist_get_selection_info
3204 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3207 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3208 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3209 src_item = folderview->summaryview->folder_item;
3211 if (item->no_select) {
3212 alertpanel_error(_("The destination folder can only be used to "
3213 "store subfolders."));
3216 /* re-check (due to acceptable possibly set for folder moves */
3217 if (!(item && item->folder && item->path && !item->no_select &&
3218 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3222 switch (gdk_drag_context_get_selected_action(drag_context)) {
3223 case GDK_ACTION_COPY:
3224 summary_copy_selected_to(folderview->summaryview, item);
3225 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3227 case GDK_ACTION_MOVE:
3228 case GDK_ACTION_DEFAULT:
3230 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3231 summary_copy_selected_to(folderview->summaryview, item);
3233 summary_move_selected_to(folderview->summaryview, item);
3234 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3237 /* comes from folderview */
3239 gboolean folder_is_normal = TRUE;
3240 gboolean copy = (GDK_ACTION_COPY ==
3241 gdk_drag_context_get_selected_action(drag_context));
3243 source = (char *)gtk_selection_data_get_data(data) + 17;
3244 if (gtk_cmclist_get_selection_info
3245 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3247 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3250 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3251 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3252 src_item = folder_find_item_from_identifier(source);
3256 src_item->stype == F_NORMAL &&
3257 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3258 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3259 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3260 !folder_has_parent_of_type(src_item, F_TRASH);
3261 if (!item || !src_item || !folder_is_normal) {
3262 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3266 folderview_move_folder(folderview, src_item, item, copy);
3267 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3269 folderview->nodes_to_recollapse = NULL;
3270 } else if (info == TARGET_MAIL_URI_LIST) {
3271 if (gtk_cmclist_get_selection_info
3272 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3275 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3277 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3278 debug_print("no node\n");
3281 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3283 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3284 debug_print("no item\n");
3287 folderview_finish_dnd(gtk_selection_data_get_data(data),
3288 drag_context, time, item);
3292 static void folderview_drag_end_cb(GtkWidget *widget,
3293 GdkDragContext *drag_context,
3294 FolderView *folderview)
3296 drag_state_stop(folderview);
3297 folderview->scroll_value = 0;
3298 g_slist_free(folderview->nodes_to_recollapse);
3299 folderview->nodes_to_recollapse = NULL;
3302 void folderview_register_popup(FolderViewPopup *fpopup)
3306 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3307 FolderView *folderview = folderviews->data;
3308 GtkActionGroup *factory;
3310 factory = create_action_group(folderview, fpopup);
3311 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3313 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3316 void folderview_unregister_popup(FolderViewPopup *fpopup)
3321 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3322 FolderView *folderview = folderviews->data;
3324 g_hash_table_remove(folderview->popups, fpopup->klass);
3326 g_hash_table_remove(folderview_popups, fpopup->klass);
3329 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3331 g_return_if_fail(folderview != NULL);
3332 g_return_if_fail(item != NULL);
3334 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3335 g_return_if_fail(ctree != NULL);
3337 GtkCMCTreeNode *node =
3338 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3339 g_return_if_fail(node != NULL);
3341 gtk_cmctree_remove_node(ctree, node);
3344 void folderview_freeze(FolderView *folderview)
3347 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3350 void folderview_thaw(FolderView *folderview)
3353 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3356 void folderview_grab_focus(FolderView *folderview)
3359 gtk_widget_grab_focus(folderview->ctree);
3362 static void folderview_header_set_displayed_columns_cb(GtkAction *gaction,
3365 prefs_folder_column_open();
3368 static gboolean folderview_header_button_pressed(GtkWidget *widget,
3372 GdkEventButton *event = (GdkEventButton *)_event;
3373 FolderView *folderview = (FolderView *)user_data;
3375 cm_return_val_if_fail(folderview != NULL, FALSE);
3377 /* Only handle single button presses. */
3378 if (event->type == GDK_2BUTTON_PRESS ||
3379 event->type == GDK_3BUTTON_PRESS)
3382 /* Handle right-click for context menu */
3383 if (event->button == 3) {
3384 gtk_menu_popup(GTK_MENU(folderview->headerpopupmenu),
3385 NULL, NULL, NULL, NULL, 3, event->time);