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);
241 GHashTable *folderview_popups;
243 static GtkActionEntry folderview_common_popup_entries[] =
245 {"FolderViewPopup", NULL, "FolderViewPopup", NULL, NULL , NULL},
246 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
247 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
248 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
249 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
250 {"FolderViewPopup/---", NULL, "---", NULL, NULL , NULL},
251 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
252 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
253 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
254 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
255 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
256 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
260 GtkTargetEntry folderview_drag_types[] =
262 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
263 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
266 void folderview_initialize(void)
268 FolderViewPopup *fpopup;
270 fpopup = g_new0(FolderViewPopup, 1);
272 fpopup->klass = "common";
273 fpopup->path = "<CommonFolder>";
274 fpopup->entries = folderview_common_popup_entries;
275 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
276 fpopup->set_sensitivity = NULL;
278 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
279 g_hash_table_insert(folderview_popups, "common", fpopup);
282 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
284 FolderViewPopup *fpopup_common;
285 GtkActionGroup *action_group;
287 action_group = cm_menu_create_action_group(
289 fpopup->entries, fpopup->n_entries,
290 (gpointer)folderview);
292 if (fpopup->toggle_entries)
293 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
294 fpopup->n_toggle_entries,
295 (gpointer)folderview);
296 if (fpopup->radio_entries)
297 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
298 fpopup->n_radio_entries, fpopup->radio_default,
299 G_CALLBACK(fpopup->radio_callback),
300 (gpointer)folderview);
302 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
303 if (fpopup_common != fpopup) {
304 gtk_action_group_add_actions(action_group, fpopup_common->entries,
305 fpopup_common->n_entries,
306 (gpointer)folderview);
307 if (fpopup_common->toggle_entries)
308 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
309 fpopup_common->n_toggle_entries,
310 (gpointer)folderview);
311 if (fpopup_common->radio_entries)
312 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
313 fpopup_common->n_radio_entries, fpopup_common->radio_default,
314 G_CALLBACK(fpopup_common->radio_callback),
315 (gpointer)folderview);
321 static void create_action_groups(gpointer key, gpointer value, gpointer data)
323 FolderView *folderview = data;
324 FolderViewPopup *fpopup = value;
325 GtkActionGroup *group;
327 group = create_action_group(folderview, fpopup);
328 g_hash_table_insert(folderview->popups, fpopup->klass, group);
331 static void folderview_column_set_titles(FolderView *folderview)
333 GtkWidget *ctree = folderview->ctree;
334 GtkWidget *label_folder;
335 GtkWidget *label_new;
336 GtkWidget *label_unread;
337 GtkWidget *label_total;
338 GtkWidget *hbox_folder;
340 GtkWidget *hbox_unread;
341 GtkWidget *hbox_total;
342 gint *col_pos = folderview->col_pos;
344 debug_print("setting titles...\n");
345 gtk_widget_realize(folderview->ctree);
346 gtk_widget_show_all(folderview->scrolledwin);
348 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
349 * instead text (text overflows making them unreadable and ugly) */
350 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
351 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
352 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
354 label_folder = gtk_label_new(_("Folder"));
355 label_new = gtk_image_new_from_pixbuf(newxpm);
356 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
357 label_total = gtk_image_new_from_pixbuf(readxpm);
359 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
361 hbox_folder = gtk_hbox_new(FALSE, 4);
362 hbox_new = gtk_hbox_new(FALSE, 4);
363 hbox_unread = gtk_hbox_new(FALSE, 4);
364 hbox_total = gtk_hbox_new(FALSE, 4);
367 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
368 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
369 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
370 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
371 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
372 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
373 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
374 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
376 gtk_widget_show_all(hbox_folder);
377 gtk_widget_show_all(hbox_new);
378 gtk_widget_show_all(hbox_unread);
379 gtk_widget_show_all(hbox_total);
382 gtk_widget_set_size_request(hbox_new, -1, 20);
383 gtk_widget_set_size_request(hbox_unread, -1, 20);
384 gtk_widget_set_size_request(hbox_total, -1, 20);
387 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
388 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
389 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
390 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
396 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
397 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
398 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
401 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
403 FolderView *folderview = (FolderView *)data;
404 GdkEventButton event;
405 if (folderview_get_selected_item(folderview) == NULL)
409 event.time = gtk_get_current_event_time();
411 folderview_set_sens_and_popup_menu(folderview, -1,
418 static GtkWidget *folderview_ctree_create(FolderView *folderview)
422 FolderColumnState *col_state;
423 FolderColumnType type;
424 gchar *titles[N_FOLDER_COLS];
426 GtkWidget *scrolledwin = folderview->scrolledwin;
428 debug_print("creating tree...\n");
429 memset(titles, 0, sizeof(titles));
431 col_state = prefs_folder_column_get_config();
432 memset(titles, 0, sizeof(titles));
434 col_pos = folderview->col_pos;
436 for (i = 0; i < N_FOLDER_COLS; i++) {
437 folderview->col_state[i] = col_state[i];
438 type = col_state[i].type;
442 titles[col_pos[F_COL_FOLDER]] = _("Folder");
443 titles[col_pos[F_COL_NEW]] = _("New");
444 titles[col_pos[F_COL_UNREAD]] = _("Unread");
445 /* TRANSLATORS: This in Number sign in American style */
446 titles[col_pos[F_COL_TOTAL]] = _("#");
448 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
451 if (prefs_common.show_col_headers == FALSE)
452 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
455 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
456 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
458 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
459 col_pos[F_COL_UNREAD],
461 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
462 col_pos[F_COL_TOTAL],
464 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
465 GTK_CMCTREE_EXPANDER_TRIANGLE);
467 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
468 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
470 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
471 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
473 /* don't let title buttons take key focus */
474 for (i = 0; i < N_FOLDER_COLS; i++) {
475 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
476 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
477 prefs_common.folder_col_size[i]);
478 gtk_cmclist_set_column_visibility
479 (GTK_CMCLIST(ctree), i, col_state[i].visible);
482 g_signal_connect(G_OBJECT(ctree), "key_press_event",
483 G_CALLBACK(folderview_key_pressed),
485 g_signal_connect(G_OBJECT(ctree), "button_press_event",
486 G_CALLBACK(folderview_button_pressed),
488 g_signal_connect(G_OBJECT(ctree), "popup-menu",
489 G_CALLBACK(folderview_popup_menu), folderview);
490 g_signal_connect(G_OBJECT(ctree), "button_release_event",
491 G_CALLBACK(folderview_button_released),
493 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
494 G_CALLBACK(folderview_selected), folderview);
496 /* drag-n-dropping folders on maemo is impractical as this
497 * opens the folder almost everytime */
498 g_signal_connect(G_OBJECT(ctree), "start_drag",
499 G_CALLBACK(folderview_start_drag), folderview);
501 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
502 G_CALLBACK(folderview_drag_data_get),
505 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
506 G_CALLBACK(folderview_tree_expanded),
508 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
509 G_CALLBACK(folderview_tree_collapsed),
512 g_signal_connect(G_OBJECT(ctree), "resize_column",
513 G_CALLBACK(folderview_col_resized),
517 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
518 folderview_drag_types, 2,
519 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
520 g_signal_connect(G_OBJECT(ctree), "drag_motion",
521 G_CALLBACK(folderview_drag_motion_cb),
523 g_signal_connect(G_OBJECT(ctree), "drag_leave",
524 G_CALLBACK(folderview_drag_leave_cb),
526 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
527 G_CALLBACK(folderview_drag_received_cb),
529 g_signal_connect(G_OBJECT(ctree), "drag_end",
530 G_CALLBACK(folderview_drag_end_cb),
533 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
538 void folderview_set_column_order(FolderView *folderview)
540 GtkWidget *ctree = folderview->ctree;
541 FolderItem *item = folderview_get_selected_item(folderview);
542 FolderItem *sel_item = NULL, *op_item = NULL;
543 GtkWidget *scrolledwin = folderview->scrolledwin;
545 if (folderview->drag_timer_id != 0) {
546 g_source_remove(folderview->drag_timer_id);
547 folderview->drag_timer_id = 0;
549 if (folderview->deferred_refresh_id != 0) {
550 g_source_remove(folderview->deferred_refresh_id);
551 folderview->deferred_refresh_id = 0;
553 if (folderview->scroll_timeout_id != 0) {
554 g_source_remove(folderview->scroll_timeout_id);
555 folderview->scroll_timeout_id = 0;
557 if (folderview->postpone_select_id != 0) {
558 g_source_remove(folderview->postpone_select_id);
559 folderview->postpone_select_id = 0;
562 if (folderview->selected)
563 sel_item = folderview_get_selected_item(folderview);
564 if (folderview->opened)
565 op_item = folderview_get_opened_item(folderview);
567 debug_print("recreating tree...\n");
568 gtk_widget_destroy(folderview->ctree);
571 folderview->ctree = ctree = folderview_ctree_create(folderview);
572 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
573 GTK_CMCLIST(ctree)->hadjustment);
574 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
575 GTK_CMCLIST(ctree)->vadjustment);
576 gtk_widget_show(ctree);
579 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
581 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
583 folderview_set(folderview);
584 folderview_column_set_titles(folderview);
586 folderview_select(folderview,item);
589 FolderView *folderview_create(MainWindow *mainwin)
591 FolderView *folderview;
592 GtkWidget *scrolledwin;
595 debug_print("Creating folder view...\n");
596 folderview = g_new0(FolderView, 1);
598 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
599 gtk_scrolled_window_set_policy
600 (GTK_SCROLLED_WINDOW(scrolledwin),
601 GTK_POLICY_AUTOMATIC,
602 prefs_common.folderview_vscrollbar_policy);
603 gtk_widget_set_size_request(scrolledwin,
604 prefs_common.folderview_width,
605 prefs_common.folderview_height);
607 folderview->scrolledwin = scrolledwin;
608 ctree = folderview_ctree_create(folderview);
610 /* create popup factories */
611 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
612 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
614 folderview->ctree = ctree;
616 folderview->folder_update_callback_id =
617 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
618 folderview->folder_item_update_callback_id =
619 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
621 gtk_widget_show_all(scrolledwin);
623 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
624 folderview_list = g_list_append(folderview_list, folderview);
626 folderview->drag_timer_id = 0;
627 folderview->deferred_refresh_id = 0;
628 folderview->scroll_timeout_id = 0;
629 folderview->postpone_select_id = 0;
634 void folderview_init(FolderView *folderview)
636 GtkWidget *ctree = folderview->ctree;
638 PangoFontDescription *normal_font;
640 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
641 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
642 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
652 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
653 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
659 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
667 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
668 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
676 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
677 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
678 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
679 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
680 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
681 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
682 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
683 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
684 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
685 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
686 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
687 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
688 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
689 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
690 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
692 normal_font = pango_font_description_from_string(NORMAL_FONT);
694 gtk_widget_modify_font(ctree, normal_font);
695 pango_font_description_free(normal_font);
697 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
700 PangoFontDescription *font_desc;
701 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
702 font_desc = pango_font_description_from_string(NORMAL_FONT);
704 if (normal_style->font_desc)
705 pango_font_description_free
706 (normal_style->font_desc);
707 normal_style->font_desc = font_desc;
709 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
710 normal_color_style = gtk_style_copy(normal_style);
711 normal_color_style->text[GTK_STATE_NORMAL] = gdk_color;
715 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
716 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
717 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
718 PangoFontDescription *font_desc;
719 font_desc = pango_font_description_from_string(NORMAL_FONT);
721 pango_font_description_free(bold_style->font_desc);
722 bold_style->font_desc = font_desc;
724 pango_font_description_set_weight
725 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
727 PangoFontDescription *font_desc;
728 font_desc = pango_font_description_from_string(BOLD_FONT);
730 if (bold_style->font_desc)
731 pango_font_description_free
732 (bold_style->font_desc);
733 bold_style->font_desc = font_desc;
736 bold_color_style = gtk_style_copy(bold_style);
737 bold_color_style->text[GTK_STATE_NORMAL] = gdk_color;
739 bold_tgtfold_style = gtk_style_copy(bold_style);
740 bold_tgtfold_style->text[GTK_STATE_NORMAL] = folderview->color_op;
744 static gboolean folderview_defer_set(gpointer data)
746 FolderView *folderview = (FolderView *)data;
747 MainWindow *mainwin = folderview->mainwin;
751 if (mainwin->lock_count)
754 debug_print("doing deferred folderview_set now\n");
755 folderview_set(folderview);
757 folderview->deferred_refresh_id = 0;
761 void folderview_set(FolderView *folderview)
763 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
764 MainWindow *mainwin = folderview->mainwin;
765 FolderItem *sel_item = NULL, *op_item = NULL;
770 if (mainwin->lock_count) {
771 if (folderview->deferred_refresh_id == 0)
772 folderview->deferred_refresh_id =
773 g_timeout_add(500, folderview_defer_set, folderview);
774 debug_print("deferred folderview_set\n");
779 debug_print("Setting folder info...\n");
780 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
782 main_window_cursor_wait(mainwin);
784 if (folderview->selected)
785 sel_item = folderview_get_selected_item(folderview);
786 if (folderview->opened)
787 op_item = folderview_get_opened_item(folderview);
789 folderview->selected = NULL;
790 folderview->opened = NULL;
792 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
793 gtk_cmclist_clear(GTK_CMCLIST(ctree));
795 folderview_set_folders(folderview);
798 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
800 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
802 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
803 main_window_cursor_normal(mainwin);
804 STATUSBAR_POP(mainwin);
808 void folderview_set_all(void)
812 for (list = folderview_list; list != NULL; list = list->next)
813 folderview_set((FolderView *)list->data);
816 void folderview_select(FolderView *folderview, FolderItem *item)
818 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
819 GtkCMCTreeNode *node;
820 GtkCMCTreeNode *old_selected = folderview->selected;
824 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
825 if (node) folderview_select_node(folderview, node);
827 if (old_selected != node)
828 folder_update_op_count();
831 static void mark_all_read_cb(GtkAction *action, gpointer data)
833 mark_all_read_unread_handler(action, data, FALSE, TRUE);
836 static void mark_all_unread_cb(GtkAction *action, gpointer data)
838 mark_all_read_unread_handler(action, data, FALSE, FALSE);
841 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
843 mark_all_read_unread_handler(action, data, TRUE, TRUE);
846 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
848 mark_all_read_unread_handler(action, data, TRUE, FALSE);
851 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
852 gboolean recursive, gboolean read)
854 FolderView *folderview = (FolderView *)data;
860 item = folderview_get_selected_item(folderview);
865 title = _("Mark all as read");
866 message = recursive? _("Do you really want to mark all mails in this "
867 "folder and its sub-folders as read?") :
868 _("Do you really want to mark all mails in this "
871 title = _("Mark all as unread");
872 message = recursive? _("Do you really want to mark all mails in this "
873 "folder and its sub-folders as unread?") :
874 _("Do you really want to mark all mails in this "
875 "folder as unread?");
877 if (prefs_common.ask_mark_all_read) {
878 val = alertpanel_full(title, message,
879 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
880 TRUE, NULL, ALERT_QUESTION);
882 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
884 else if (val & G_ALERTDISABLE)
885 prefs_common.ask_mark_all_read = FALSE;
888 folder_item_update_freeze();
889 if (folderview->summaryview->folder_item != item && !recursive)
890 summary_lock(folderview->summaryview);
892 summary_freeze(folderview->summaryview);
896 folderutils_mark_all_read_recursive(item, TRUE);
898 folderutils_mark_all_read(item, TRUE);
901 folderutils_mark_all_read_recursive(item, FALSE);
903 folderutils_mark_all_read(item, FALSE);
905 if (folderview->summaryview->folder_item != item && !recursive)
906 summary_unlock(folderview->summaryview);
908 summary_thaw(folderview->summaryview);
909 folder_item_update_thaw();
912 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
914 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
916 cm_return_if_fail(node != NULL);
918 if (folderview->open_folder) {
922 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
923 gtkut_ctree_expand_parent_all(ctree, node);
925 folderview->open_folder = TRUE;
926 gtkut_ctree_set_focus_row(ctree, node);
927 gtk_cmctree_select(ctree, node);
928 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
929 if ((folderview->summaryview->folder_item &&
930 folderview->summaryview->folder_item->total_msgs > 0) ||
931 prefs_common.layout_mode == SMALL_LAYOUT)
932 summary_select_node(folderview->summaryview,
933 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
935 gtk_widget_grab_focus(folderview->ctree);
938 void folderview_unselect(FolderView *folderview)
940 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
942 (GTK_CMCTREE(folderview->ctree), folderview->opened);
944 folderview->selected = folderview->opened = NULL;
947 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
948 GtkCMCTreeNode *node,
954 node = gtkut_ctree_node_next(ctree, node);
956 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
958 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
959 item = gtk_cmctree_node_get_row_data(ctree, node);
962 if (item->stype == F_TRASH || item->stype == F_DRAFT)
966 if(item->unread_msgs > 0)
970 if(item->new_msgs > 0)
974 if(item->marked_msgs > 0)
978 if(item->total_msgs > 0)
987 void folderview_select_next_with_flag(FolderView *folderview,
990 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
991 GtkCMCTreeNode *node = NULL;
992 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
996 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
999 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1002 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1005 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1009 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1011 folderview_select_node(folderview, node);
1015 if (!folderview->opened ||
1016 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1020 /* search again from the first node */
1021 node = folderview_find_next_with_flag(ctree, NULL, flag);
1023 folderview_select_node(folderview, node);
1026 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1029 FolderItem *folderview_get_selected_item(FolderView *folderview)
1031 g_return_val_if_fail(folderview != NULL, NULL);
1032 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1034 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1036 if (!folderview->selected) return NULL;
1037 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1040 FolderItem *folderview_get_opened_item(FolderView *folderview)
1042 g_return_val_if_fail(folderview != NULL, NULL);
1043 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1045 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1047 if (!folderview->opened) return NULL;
1048 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1051 static void folderview_set_folders(FolderView *folderview)
1054 list = folder_get_list();
1056 for (; list != NULL; list = list->next) {
1057 folderview_append_folder(folderview, FOLDER(list->data));
1061 static gchar *get_scan_str(FolderItem *item)
1064 return g_strdup_printf(_("Scanning folder %s/%s..."),
1065 item->folder->name, item->path);
1067 return g_strdup_printf(_("Scanning folder %s..."),
1068 item->folder->name);
1070 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1074 for (list = folderview_list; list != NULL; list = list->next) {
1075 FolderView *folderview = (FolderView *)list->data;
1076 MainWindow *mainwin = folderview->mainwin;
1077 gchar *str = get_scan_str(item);
1079 STATUSBAR_PUSH(mainwin, str);
1080 STATUSBAR_POP(mainwin);
1085 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1088 MainWindow *mainwin = mainwindow_get_mainwindow();
1089 FolderView *folderview = NULL;
1090 GtkAdjustment *pos = NULL;
1093 cm_return_if_fail(folder != NULL);
1095 if (!folder->klass->scan_tree) return;
1098 alertpanel_full(_("Rebuild folder tree"),
1099 _("Rebuilding the folder tree will remove "
1100 "local caches. Do you want to continue?"),
1101 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
1102 FALSE, NULL, ALERT_WARNING)
1103 != G_ALERTALTERNATE) {
1109 window = label_window_create(_("Rebuilding folder tree..."));
1111 window = label_window_create(_("Scanning folder tree..."));
1114 folderview = mainwin->folderview;
1117 pos = gtk_scrolled_window_get_vadjustment(
1118 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1119 height = gtk_adjustment_get_value(pos);
1122 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1123 folder_scan_tree(folder, rebuild);
1124 folder_set_ui_func(folder, NULL, NULL);
1126 folderview_set_all();
1129 pos = gtk_scrolled_window_get_vadjustment(
1130 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1131 gtk_adjustment_set_value(pos, height);
1132 gtk_adjustment_changed(pos);
1134 label_window_destroy(window);
1138 /** folderview_check_new()
1139 * Scan and update the folder and return the
1140 * count the number of new messages since last check.
1141 * \param folder the folder to check for new messages
1142 * \return the number of new messages since last check
1144 gint folderview_check_new(Folder *folder)
1148 FolderView *folderview;
1150 GtkCMCTreeNode *node;
1152 gint former_new_msgs = 0;
1153 gint former_new = 0, former_unread = 0, former_total;
1155 for (list = folderview_list; list != NULL; list = list->next) {
1156 folderview = (FolderView *)list->data;
1157 ctree = GTK_CMCTREE(folderview->ctree);
1158 folderview->scanning_folder = folder;
1160 main_window_lock(folderview->mainwin);
1162 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1163 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1165 item = gtk_cmctree_node_get_row_data(ctree, node);
1166 if (!item || !item->path || !item->folder) continue;
1167 if (item->no_select) continue;
1168 if (folder && folder != item->folder) continue;
1169 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1170 if (!item->prefs->newmailcheck) continue;
1171 if (item->processing_pending == TRUE) {
1172 debug_print("skipping %s, processing pending\n",
1173 item->path ? item->path : item->name);
1176 if (item->scanning != ITEM_NOT_SCANNING) {
1177 debug_print("skipping %s, scanning\n",
1178 item->path ? item->path : item->name);
1182 str = get_scan_str(item);
1184 STATUSBAR_PUSH(folderview->mainwin, str);
1188 folderview_scan_tree_func(item->folder, item, NULL);
1189 former_new = item->new_msgs;
1190 former_unread = item->unread_msgs;
1191 former_total = item->total_msgs;
1193 if (item->folder->klass->scan_required &&
1194 (item->folder->klass->scan_required(item->folder, item) ||
1195 item->folder->inbox == item ||
1196 item->opened == TRUE ||
1197 item->processing_pending == TRUE)) {
1198 if (folder_item_scan(item) < 0) {
1200 summaryview_unlock(folderview->summaryview, item);
1201 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1202 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1203 item->path ? item->path:item->name);
1204 STATUSBAR_POP(folderview->mainwin);
1206 } else if (!FOLDER_IS_LOCAL(folder)) {
1207 STATUSBAR_POP(folderview->mainwin);
1212 } else if (!item->folder->klass->scan_required) {
1213 if (folder_item_scan(item) < 0) {
1214 summaryview_unlock(folderview->summaryview, item);
1215 if (folder && !FOLDER_IS_LOCAL(folder)) {
1216 STATUSBAR_POP(folderview->mainwin);
1221 if (former_new != item->new_msgs ||
1222 former_unread != item->unread_msgs ||
1223 former_total != item->total_msgs)
1224 folderview_update_node(folderview, node);
1226 new_msgs += item->new_msgs;
1227 former_new_msgs += former_new;
1228 STATUSBAR_POP(folderview->mainwin);
1230 folderview->scanning_folder = NULL;
1231 main_window_unlock(folderview->mainwin);
1235 folder_write_list();
1236 /* Number of new messages since last check is the just the difference
1237 * between former_new_msgs and new_msgs. If new_msgs is less than
1238 * former_new_msgs, that would mean another session accessed the folder
1239 * and the result is not well defined.
1241 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1245 void folderview_check_new_all(void)
1249 FolderView *folderview;
1251 folderview = (FolderView *)folderview_list->data;
1254 main_window_lock(folderview->mainwin);
1255 window = label_window_create
1256 (_("Checking for new messages in all folders..."));
1258 list = folder_get_list();
1259 for (; list != NULL; list = list->next) {
1260 Folder *folder = list->data;
1262 folderview_check_new(folder);
1265 folder_write_list();
1266 folderview_set_all();
1268 label_window_destroy(window);
1269 main_window_unlock(folderview->mainwin);
1273 static gboolean folderview_have_children_sub(FolderView *folderview,
1279 if (!item || !item->folder || !item->folder->node)
1282 node = item->folder->node;
1284 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1285 node = node->children;
1287 if (in_sub && item->total_msgs > 0) {
1291 while (node != NULL) {
1292 if (node && node->data) {
1293 FolderItem *next_item = (FolderItem*) node->data;
1295 if (folderview_have_children_sub(folderview,
1304 static gboolean folderview_have_children(FolderView *folderview,
1307 return folderview_have_children_sub(folderview, item, FALSE);
1310 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1316 if (!item || !item->folder || !item->folder->node)
1319 node = item->folder->node;
1321 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1322 node = node->children;
1325 (item->new_msgs > 0 ||
1326 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1330 while (node != NULL) {
1331 if (node && node->data) {
1332 FolderItem *next_item = (FolderItem*) node->data;
1334 if (folderview_have_new_children_sub(folderview,
1343 static gboolean folderview_have_new_children(FolderView *folderview,
1346 return folderview_have_new_children_sub(folderview, item, FALSE);
1349 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1355 if (!item || !item->folder || !item->folder->node)
1358 node = item->folder->node;
1360 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1361 node = node->children;
1364 (item->unread_msgs > 0 ||
1365 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1369 while (node != NULL) {
1370 if (node && node->data) {
1371 FolderItem *next_item = (FolderItem*) node->data;
1373 if (folderview_have_unread_children_sub(folderview,
1383 static gboolean folderview_have_unread_children(FolderView *folderview,
1386 return folderview_have_unread_children_sub(folderview, item, FALSE);
1389 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1395 if (!item || !item->folder || !item->folder->node) {
1399 node = item->folder->node;
1401 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1402 node = node->children;
1405 (((item->total_msgs > 0) &&
1406 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1410 while (node != NULL) {
1411 if (node && node->data) {
1412 FolderItem *next_item = (FolderItem*) node->data;
1414 if (folderview_have_read_children_sub(folderview,
1425 static gboolean folderview_have_read_children(FolderView *folderview,
1428 return folderview_have_read_children_sub(folderview, item, FALSE);
1431 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1437 if (!item || !item->folder || !item->folder->node)
1440 node = item->folder->node;
1442 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1443 node = node->children;
1445 if (in_sub && item->search_match){
1449 while (node != NULL) {
1450 if (node && node->data) {
1451 FolderItem *next_item = (FolderItem*) node->data;
1453 if (folderview_have_matching_children_sub(folderview,
1463 static gboolean folderview_have_matching_children(FolderView *folderview,
1466 return folderview_have_matching_children_sub(folderview, item, FALSE);
1469 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1475 if (!item || !item->folder || !item->folder->node)
1478 node = item->folder->node;
1480 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1481 node = node->children;
1483 if (item->marked_msgs != 0) {
1487 while (node != NULL) {
1488 if (node && node->data) {
1489 FolderItem *next_item = (FolderItem*) node->data;
1491 if (folderview_have_marked_children_sub(folderview,
1500 static gboolean folderview_have_marked_children(FolderView *folderview,
1503 return folderview_have_marked_children_sub(folderview, item, FALSE);
1506 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1508 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1509 GtkStyle *style = NULL;
1510 GtkStyle *color_style = NULL;
1512 GdkPixbuf *xpm, *openxpm;
1513 static GdkPixbuf *searchicon;
1514 gboolean mark = FALSE;
1517 gboolean add_unread_mark;
1518 gboolean add_sub_match_mark;
1519 gboolean use_bold, use_color;
1520 gint *col_pos = folderview->col_pos;
1521 SpecialFolderItemType stype;
1522 gboolean copiedstyle = FALSE;
1524 item = gtk_cmctree_node_get_row_data(ctree, node);
1525 cm_return_if_fail(item != NULL);
1527 if (!GTK_CMCTREE_ROW(node)->expanded)
1528 mark = folderview_have_marked_children(folderview, item);
1530 mark = (item->marked_msgs != 0);
1532 stype = item->stype;
1533 if (stype == F_NORMAL) {
1534 if (folder_has_parent_of_type(item, F_TRASH))
1536 else if (folder_has_parent_of_type(item, F_DRAFT))
1538 else if (folder_has_parent_of_type(item, F_OUTBOX))
1540 else if (folder_has_parent_of_type(item, F_QUEUE))
1545 if (item->hide_read_msgs || item->hide_read_threads) {
1546 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1547 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1549 xpm = mark?m_inboxxpm:inboxxpm;
1550 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1554 if (item->hide_read_msgs || item->hide_read_threads) {
1555 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1556 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1558 xpm = mark?m_outboxxpm:outboxxpm;
1559 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1563 if (item->hide_read_msgs || item->hide_read_threads) {
1564 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1565 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1567 xpm = mark?m_queuexpm:queuexpm;
1568 openxpm = mark?m_queueopenxpm:queueopenxpm;
1572 if (item->hide_read_msgs || item->hide_read_threads) {
1573 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1574 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1576 xpm = mark?m_trashxpm:trashxpm;
1577 openxpm = mark?m_trashopenxpm:trashopenxpm;
1581 xpm = mark?m_draftsxpm:draftsxpm;
1582 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1586 FOLDER_TYPE(item->folder) == F_IMAP &&
1587 item->folder->account->imap_subsonly) {
1588 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1589 openxpm = foldersubsopenxpm;
1590 } else if (item->no_select) {
1591 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1592 openxpm = foldernoselectopenxpm;
1593 } else if (item->hide_read_msgs || item->hide_read_threads) {
1594 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1595 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1597 xpm = mark?m_folderxpm:folderxpm;
1598 openxpm = mark?m_folderopenxpm:folderopenxpm;
1602 name = folder_item_get_name(item);
1604 if (!GTK_CMCTREE_ROW(node)->expanded) {
1605 add_unread_mark = folderview_have_unread_children(
1607 add_sub_match_mark = folderview_have_matching_children(
1610 add_unread_mark = FALSE;
1611 add_sub_match_mark = FALSE;
1614 if (item->search_match) {
1616 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1619 xpm = openxpm = searchicon;
1623 if (prefs_common.display_folder_unread) {
1624 if (folder_has_parent_of_type(item, F_QUEUE)) {
1625 /* only total_msgs matters here */
1626 if (item->total_msgs > 0) {
1627 /* show total number (should be equal to the unread number)
1629 str = g_strdup_printf("%s (%d%s%s)",
1630 name, item->total_msgs,
1631 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1632 (item->unreadmarked_msgs > 0) ? "!" : "");
1635 if (prefs_common.display_folder_unread == 1) {
1636 if (item->unread_msgs > 0) {
1637 /* show unread number and signs */
1638 str = g_strdup_printf("%s (%d%s%s)",
1639 name, item->unread_msgs,
1640 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1641 (item->unreadmarked_msgs > 0) ? "!" : "");
1644 if (item->total_msgs > 0) {
1645 /* show unread number, total number and signs if any */
1646 str = g_strdup_printf("%s (%d/%d%s%s)",
1647 name, item->unread_msgs, item->total_msgs,
1648 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1649 (item->unreadmarked_msgs > 0) ? "!" : "");
1653 if ((str == NULL) &&
1654 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1655 /* no unread/total numbers, but at least one sign */
1656 str = g_strdup_printf("%s (%s%s)",
1658 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1659 (item->unreadmarked_msgs > 0) ? "!" : "");
1663 /* last fallback, folder name only or with +! sign */
1664 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1665 str = g_strdup_printf("%s%s",
1667 } else if (item->unreadmarked_msgs > 0) {
1668 str = g_strdup_printf("%s%s",
1670 } else if (add_sub_match_mark) {
1671 str = g_strdup_printf("%s%s",
1674 str = g_strdup_printf("%s", name);
1677 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1679 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1683 if (!folder_item_parent(item)) {
1684 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1685 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1689 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1693 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1694 folder_has_parent_of_type(item, F_TRASH)) {
1695 use_bold = use_color = FALSE;
1696 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1697 GSList *list = folder_item_get_msg_list(item);
1699 use_bold = use_color = FALSE;
1700 for (cur = list; cur; cur = cur->next) {
1701 MsgInfo *msginfo = (MsgInfo *)cur->data;
1702 if (!MSG_IS_DELETED(msginfo->flags)) {
1703 /* highlight queue folder if there are any messages */
1704 use_bold = use_color = TRUE;
1708 if (!GTK_CMCTREE_ROW(node)->expanded &&
1709 use_bold == FALSE &&
1710 folderview_have_children(folderview, item))
1711 use_bold = use_color = TRUE;
1712 procmsg_msg_list_free(list);
1714 /* if unread messages exist, print with bold font */
1715 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1717 /* if new messages exist, print with colored letter */
1719 (item->new_msgs > 0) ||
1721 folderview_have_new_children(folderview, item));
1724 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1729 if (item->prefs->color > 0 && !use_color) {
1730 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1731 color_style = gtk_style_copy(bold_style);
1733 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1734 style = color_style;
1735 } else if (use_color) {
1736 style = bold_color_style;
1739 if (item->op_count > 0) {
1740 style = bold_tgtfold_style;
1742 } else if (use_color) {
1743 style = normal_color_style;
1744 gtk_cmctree_node_set_foreground(ctree, node,
1745 &folderview->color_new);
1746 } else if (item->op_count > 0) {
1747 style = bold_tgtfold_style;
1748 } else if (item->prefs->color > 0) {
1750 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1751 color_style = gtk_style_copy(normal_style);
1753 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1754 style = color_style;
1756 style = normal_style;
1759 gtk_cmctree_node_set_row_style(ctree, node, style);
1761 g_object_unref(style);
1763 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1764 folderview_update_node(folderview, node);
1767 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1770 FolderView *folderview;
1772 GtkCMCTreeNode *node;
1774 cm_return_if_fail(item != NULL);
1776 for (list = folderview_list; list != NULL; list = list->next) {
1777 folderview = (FolderView *)list->data;
1778 ctree = GTK_CMCTREE(folderview->ctree);
1780 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1781 if (node && item->search_match != matches) {
1782 item->search_match = matches;
1783 folderview_update_node(folderview, node);
1788 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1790 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1791 FolderView *folderview = (FolderView *)data;
1793 GtkCMCTreeNode *node;
1794 cm_return_val_if_fail(update_info != NULL, TRUE);
1795 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1796 cm_return_val_if_fail(folderview != NULL, FALSE);
1798 ctree = GTK_CMCTREE(folderview->ctree);
1800 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1803 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1804 folderview_update_node(folderview, node);
1806 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1807 update_info->item == folderview->summaryview->folder_item &&
1808 update_info->item != NULL)
1809 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1810 summary_show(folderview->summaryview, update_info->item);
1816 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1817 GNode *gnode, GtkCMCTreeNode *cnode,
1820 FolderView *folderview = (FolderView *)data;
1821 FolderItem *item = FOLDER_ITEM(gnode->data);
1823 cm_return_val_if_fail(item != NULL, FALSE);
1825 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1826 folderview_update_node(folderview, cnode);
1831 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1834 FolderView *folderview = (FolderView *)data;
1837 if (GTK_CMCTREE_ROW(node)->children) {
1838 item = gtk_cmctree_node_get_row_data(ctree, node);
1839 cm_return_if_fail(item != NULL);
1841 if (!item->collapsed)
1842 gtk_cmctree_expand(ctree, node);
1844 folderview_update_node(folderview, node);
1848 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1849 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1852 GtkCMCTreeNode *node, *parent, *sibling;
1854 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1856 g_warning("%s not found.", item->path);
1858 parent = GTK_CMCTREE_ROW(node)->parent;
1859 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1860 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1862 sibling = GTK_CMCTREE_ROW(parent)->children;
1866 tmp = gtk_cmctree_node_get_row_data
1868 if (tmp && tmp->stype != F_NORMAL)
1869 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1873 if (node != sibling)
1874 gtk_cmctree_move(ctree, node, parent, sibling);
1881 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1884 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1885 GtkCMCTreeNode *prev = NULL;
1887 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1888 gtk_sctree_sort_recursive(ctree, root);
1889 if (root && GTK_CMCTREE_ROW(root)->parent) {
1890 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1893 set_special_folder(ctree, folder->inbox, root, &prev);
1894 set_special_folder(ctree, folder->outbox, root, &prev);
1895 set_special_folder(ctree, folder->draft, root, &prev);
1896 set_special_folder(ctree, folder->queue, root, &prev);
1897 set_special_folder(ctree, folder->trash, root, &prev);
1898 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1901 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1903 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1904 GtkCMCTreeNode *root;
1906 cm_return_if_fail(folder != NULL);
1908 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1909 folderview_gnode_func, folderview);
1910 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1912 folderview_sort_folders(folderview, root, folder);
1915 /* callback functions */
1916 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1917 GdkEventButton *event)
1921 FolderViewPopup *fpopup;
1922 GtkActionGroup *action_group;
1924 FolderItem *special_trash = NULL, *special_queue = NULL;
1926 GtkUIManager *ui_manager = gtk_ui_manager_new();
1928 if (folderview->ui_manager)
1929 g_object_unref(folderview->ui_manager);
1931 folderview->ui_manager = ui_manager;
1932 item = folderview_get_selected_item(folderview);
1934 cm_return_if_fail(item != NULL);
1935 cm_return_if_fail(item->folder != NULL);
1936 folder = item->folder;
1938 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1941 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1943 fpopup = g_hash_table_lookup(folderview_popups, "common");
1944 action_group = g_hash_table_lookup(folderview->popups, "common");
1947 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1948 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1949 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1951 if (fpopup->add_menuitems)
1952 fpopup->add_menuitems(ui_manager, item);
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1960 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1961 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1962 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1964 if (fpopup->set_sensitivity != NULL)
1965 fpopup->set_sensitivity(ui_manager, item);
1967 if (NULL != (ac = account_find_from_item(item))) {
1968 special_trash = account_get_special_folder(ac, F_TRASH);
1969 special_queue = account_get_special_folder(ac, F_QUEUE);
1972 if ((item == folder->trash || item == special_trash
1973 || folder_has_parent_of_type(item, F_TRASH))) {
1974 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1975 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1978 if ((item == folder->queue || item == special_queue
1979 || folder_has_parent_of_type(item, F_QUEUE))) {
1980 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1981 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1984 #define SET_SENS(name, sens) \
1985 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1987 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1988 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1989 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1990 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1991 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1992 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1993 folderview->selected == folderview->opened);
1994 SET_SENS("FolderViewPopup/Properties", TRUE);
1996 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1997 item->total_msgs >= 1 && !item->processing_pending);
1998 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1999 !item->no_select && !item->processing_pending);
2001 if (item == folder->trash || item == special_trash
2002 || folder_has_parent_of_type(item, F_TRASH)) {
2003 GSList *msglist = folder_item_get_msg_list(item);
2004 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2005 procmsg_msg_list_free(msglist);
2007 if (item == folder->queue || item == special_queue
2008 || folder_has_parent_of_type(item, F_QUEUE)) {
2009 GSList *msglist = folder_item_get_msg_list(item);
2010 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2011 procmsg_msg_list_free(msglist);
2015 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2016 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2017 g_signal_connect(G_OBJECT(popup), "selection_done",
2018 G_CALLBACK(folderview_popup_close),
2020 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2021 event->button, event->time);
2024 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2025 FolderView *folderview)
2027 GtkCMCList *clist = GTK_CMCLIST(ctree);
2028 gint prev_row = -1, row = -1, column = -1;
2030 if (!event) return FALSE;
2031 if (event->window != clist->clist_window) return FALSE;
2033 if (event->button == 1 || event->button == 2) {
2034 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2035 folderview->open_folder = TRUE;
2037 if (event->type == GDK_2BUTTON_PRESS) {
2038 if (clist->selection) {
2039 GtkCMCTreeNode *node;
2041 node = GTK_CMCTREE_NODE(clist->selection->data);
2043 gtk_cmctree_toggle_expansion(
2046 folderview->open_folder = FALSE;
2053 if (event->button == 2 || event->button == 3) {
2055 if (clist->selection) {
2056 GtkCMCTreeNode *node;
2058 node = GTK_CMCTREE_NODE(clist->selection->data);
2060 prev_row = gtkut_ctree_get_nth_from_node
2061 (GTK_CMCTREE(ctree), node);
2064 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2067 if (prev_row != row) {
2068 gtk_cmclist_unselect_all(clist);
2069 if (event->button == 2)
2070 folderview_select_node
2072 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2075 gtk_cmclist_select_row(clist, row, column);
2079 if (event->button != 3) return FALSE;
2081 folderview_set_sens_and_popup_menu(folderview, row, event);
2085 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2086 FolderView *folderview)
2088 int row = -1, column = -1;
2090 if (!event) return FALSE;
2092 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2095 if (event->button == 1 && folderview->open_folder == FALSE &&
2096 folderview->opened != NULL) {
2097 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2098 folderview->opened);
2099 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2105 #define BREAK_ON_MODIFIER_KEY() \
2106 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2108 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2109 FolderView *folderview)
2111 GtkCMCTreeNode *node;
2114 if (!event) return FALSE;
2116 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2119 switch (event->keyval) {
2121 if (folderview->selected) {
2122 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2123 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2124 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2125 folderview->selected);
2127 folderview_select_node(folderview,
2128 folderview->selected);
2132 case GDK_KEY_Return:
2133 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2134 gtk_cmctree_toggle_expansion(
2135 GTK_CMCTREE(folderview->ctree),
2136 folderview->selected);
2140 case GDK_KEY_Return:
2141 case GDK_KEY_KP_Enter:
2142 if (folderview->selected)
2143 folderview_select_node(folderview, folderview->selected);
2147 BREAK_ON_MODIFIER_KEY();
2148 if (folderview->selected) {
2149 if (folderview->opened == folderview->selected &&
2150 (!folderview->summaryview->folder_item ||
2151 folderview->summaryview->folder_item->total_msgs == 0))
2152 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2154 folderview_select_node(folderview,
2155 folderview->selected);
2159 if (folderview->selected) {
2160 /* If the folder is expanded and can be collapsed, do that... */
2161 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2162 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2163 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2164 folderview->selected);
2166 /* ...otherwise, move cursor to its parent node. */
2167 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2168 folderview->selected))) {
2169 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2170 NULL, folder_item_parent(item)))) {
2171 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2172 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2173 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2182 if (event->keyval == GDK_KEY_Home)
2183 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2185 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2186 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2188 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2190 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2191 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2201 typedef struct _PostponedSelectData
2204 GtkCMCTreeNode *row;
2206 FolderView *folderview;
2207 } PostponedSelectData;
2209 static gboolean postpone_select(void *data)
2211 PostponedSelectData *psdata = (PostponedSelectData *)data;
2212 debug_print("trying again\n");
2214 psdata->folderview->postpone_select_id = 0;
2215 psdata->folderview->open_folder = TRUE;
2216 main_window_cursor_normal(psdata->folderview->mainwin);
2217 STATUSBAR_POP(psdata->folderview->mainwin);
2218 folderview_selected(psdata->ctree, psdata->row,
2219 psdata->column, psdata->folderview);
2224 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2226 if (folderview->opened) {
2228 folderview->opened = NULL;
2232 FolderItem *olditem =
2233 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2234 folderview->opened);
2236 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2237 olditem->path ? olditem->path:olditem->name);
2238 /* will be null if we just moved the previously opened folder */
2239 STATUSBAR_PUSH(folderview->mainwin, buf);
2240 main_window_cursor_wait(folderview->mainwin);
2242 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2243 summary_show(folderview->summaryview, NULL);
2244 folder_item_close(olditem);
2245 main_window_cursor_normal(folderview->mainwin);
2246 STATUSBAR_POP(folderview->mainwin);
2247 if (olditem->folder->klass->item_closed)
2248 olditem->folder->klass->item_closed(olditem);
2253 if (folderview->opened &&
2254 !GTK_CMCTREE_ROW(folderview->opened)->children)
2255 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2257 folderview->opened = NULL;
2259 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2260 gint column, FolderView *folderview)
2262 static gboolean can_select = TRUE; /* exclusive lock */
2267 GtkCMCTreeNode *old_opened = folderview->opened;
2269 folderview->selected = row;
2271 debug_print("newly selected %p, opened %p\n", folderview->selected,
2272 folderview->opened);
2273 if (folderview->opened == row) {
2274 folderview->open_folder = FALSE;
2279 item = gtk_cmctree_node_get_row_data(ctree, row);
2282 folderview->open_folder = FALSE;
2286 if (!can_select || summary_is_locked(folderview->summaryview)) {
2287 if (folderview->opened) {
2288 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2289 gtk_cmctree_select(ctree, folderview->opened);
2291 folderview->open_folder = FALSE;
2296 if (!folderview->open_folder) {
2303 /* Save cache for old folder */
2304 /* We don't want to lose all caches if app crashes */
2305 /* Resets folderview->opened to NULL */
2306 folderview_close_opened(folderview, FALSE);
2308 /* CLAWS: set compose button type: news folder items
2309 * always have a news folder as parent */
2311 toolbar_set_compose_button
2312 (folderview->mainwin->toolbar,
2313 FOLDER_TYPE(item->folder) == F_NEWS ?
2314 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2317 debug_print("Folder %s is selected\n", item->path);
2319 if (!GTK_CMCTREE_ROW(row)->children)
2320 gtk_cmctree_expand(ctree, row);
2322 /* ungrab the mouse event */
2323 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2324 gtk_grab_remove(GTK_WIDGET(ctree));
2325 if (gdk_pointer_is_grabbed())
2326 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2330 /* TODO: wwp: avoid displaying (null) in the status bar */
2331 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2332 item->path : "(null)");
2333 debug_print("%s\n", buf);
2334 STATUSBAR_PUSH(folderview->mainwin, buf);
2337 main_window_cursor_wait(folderview->mainwin);
2339 if (folderview->scanning_folder == item->folder) {
2342 res = folder_item_open(item);
2345 if (res == -1 && item->no_select == FALSE) {
2346 main_window_cursor_normal(folderview->mainwin);
2347 STATUSBAR_POP(folderview->mainwin);
2349 alertpanel_error(_("Folder could not be opened."));
2351 folderview->open_folder = FALSE;
2355 } else if (res == -2 && item->no_select == FALSE) {
2356 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2357 data->ctree = ctree;
2359 data->column = column;
2360 data->folderview = folderview;
2361 debug_print("postponing open of %s till end of scan\n",
2362 item->path ? item->path:item->name);
2363 folderview->open_folder = FALSE;
2365 if (folderview->postpone_select_id != 0)
2366 g_source_remove(folderview->postpone_select_id);
2367 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2372 main_window_cursor_normal(folderview->mainwin);
2375 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2376 opened = summary_show(folderview->summaryview, item);
2378 folder_clean_cache_memory(item);
2381 gtkut_ctree_set_focus_row(ctree, old_opened);
2382 gtk_cmctree_select(ctree, old_opened);
2383 folderview->opened = old_opened;
2385 folderview->opened = row;
2386 if (gtk_cmctree_node_is_visible(ctree, row)
2387 != GTK_VISIBILITY_FULL)
2388 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2391 STATUSBAR_POP(folderview->mainwin);
2393 folderview->open_folder = FALSE;
2398 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2399 FolderView *folderview)
2403 item = gtk_cmctree_node_get_row_data(ctree, node);
2404 cm_return_if_fail(item != NULL);
2405 item->collapsed = FALSE;
2406 folderview_update_node(folderview, node);
2409 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2410 FolderView *folderview)
2414 item = gtk_cmctree_node_get_row_data(ctree, node);
2415 cm_return_if_fail(item != NULL);
2416 item->collapsed = TRUE;
2417 folderview_update_node(folderview, node);
2420 static void folderview_popup_close(GtkMenuShell *menu_shell,
2421 FolderView *folderview)
2423 if (!folderview->opened) return;
2425 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2428 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2429 FolderView *folderview)
2431 FolderColumnType type = folderview->col_state[column].type;
2433 prefs_common.folder_col_size[type] = width;
2436 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2438 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2439 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2440 GtkCMCTreeNode *node, *parent_node;
2441 gint *col_pos = folderview->col_pos;
2442 FolderItemUpdateData hookdata;
2444 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2445 if (parent_node == NULL)
2448 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2450 text[col_pos[F_COL_FOLDER]] = item->name;
2451 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2456 gtk_cmctree_expand(ctree, parent_node);
2457 gtk_cmctree_node_set_row_data(ctree, node, item);
2459 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2460 folderview_sort_folders(folderview, parent_node, item->folder);
2462 hookdata.item = item;
2463 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2464 hookdata.msg = NULL;
2465 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2467 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2470 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2472 FolderView *folderview = (FolderView *)data;
2474 GSList *mlist = NULL;
2476 FolderItem *special_trash = NULL;
2479 if (!folderview->selected) return;
2480 item = folderview_get_selected_item(folderview);
2481 cm_return_if_fail(item != NULL);
2482 cm_return_if_fail(item->folder != NULL);
2484 if (NULL != (ac = account_find_from_item(item)))
2485 special_trash = account_get_special_folder(ac, F_TRASH);
2487 if (item != item->folder->trash && item != special_trash
2488 && !folder_has_parent_of_type(item, F_TRASH)) return;
2490 if (prefs_common.ask_on_clean) {
2491 if (alertpanel(_("Empty trash"),
2492 _("Delete all messages in trash?"),
2493 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2494 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2498 mlist = folder_item_get_msg_list(item);
2500 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2501 MsgInfo * msginfo = (MsgInfo *) cur->data;
2502 if (MSG_IS_LOCKED(msginfo->flags))
2504 /* is it partially received? (partial_recv isn't cached) */
2505 if (msginfo->total_size != 0 &&
2506 msginfo->size != (off_t)msginfo->total_size)
2507 partial_mark_for_delete(msginfo);
2509 procmsg_msg_list_free(mlist);
2511 folder_item_remove_all_msg(item);
2514 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2516 FolderView *folderview = (FolderView *)data;
2518 FolderItem *special_queue = NULL;
2520 gchar *errstr = NULL;
2522 if (!folderview->selected) return;
2523 item = folderview_get_selected_item(folderview);
2524 cm_return_if_fail(item != NULL);
2525 cm_return_if_fail(item->folder != NULL);
2527 if (NULL != (ac = account_find_from_item(item)))
2528 special_queue = account_get_special_folder(ac, F_QUEUE);
2530 if (item != item->folder->queue && item != special_queue
2531 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2533 if (procmsg_queue_is_empty(item))
2536 if (prefs_common.work_offline)
2537 if (alertpanel(_("Offline warning"),
2538 _("You're working offline. Override?"),
2539 GTK_STOCK_NO, GTK_STOCK_YES,
2540 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2543 /* ask for confirmation before sending queued messages only
2544 in online mode and if there is at least one message queued
2545 in any of the folder queue
2547 if (prefs_common.confirm_send_queued_messages) {
2548 if (!prefs_common.work_offline) {
2549 if (alertpanel(_("Send queued messages"),
2550 _("Send all queued messages?"),
2551 GTK_STOCK_CANCEL, _("_Send"),
2552 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2557 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2559 alertpanel_error_log(_("Some errors occurred while "
2560 "sending queued messages."));
2562 alertpanel_error_log(_("Some errors occurred "
2563 "while sending queued messages:\n%s"), errstr);
2569 static void folderview_search_cb(GtkAction *action, gpointer data)
2571 FolderView *folderview = (FolderView *)data;
2572 summary_search(folderview->summaryview);
2575 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2577 FolderView *folderview = (FolderView *)data;
2580 if (!folderview->selected) return;
2582 item = folderview_get_selected_item(folderview);
2583 cm_return_if_fail(item != NULL);
2584 cm_return_if_fail(item->folder != NULL);
2586 item->processing_pending = TRUE;
2587 folder_item_apply_processing(item);
2588 item->processing_pending = FALSE;
2591 static void folderview_property_cb(GtkAction *action, gpointer data)
2593 FolderView *folderview = (FolderView *)data;
2596 if (!folderview->selected) return;
2598 item = folderview_get_selected_item(folderview);
2599 cm_return_if_fail(item != NULL);
2600 cm_return_if_fail(item->folder != NULL);
2602 prefs_folder_item_open(item);
2605 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2607 GSList *list = NULL;
2608 GSList *done = NULL;
2609 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2611 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2612 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2613 && list->data != node) {
2614 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2615 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2618 for (list = done; list != NULL; list = g_slist_next(list)) {
2619 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2625 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2626 FolderItem *to_folder, gboolean copy)
2628 FolderItem *new_folder = NULL;
2632 cm_return_if_fail(folderview != NULL);
2633 cm_return_if_fail(from_folder != NULL);
2634 cm_return_if_fail(to_folder != NULL);
2636 if (prefs_common.warn_dnd) {
2637 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2638 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2639 from_folder->name, to_folder->name);
2640 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2641 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2642 TRUE, NULL, ALERT_QUESTION);
2645 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2647 else if (status & G_ALERTDISABLE)
2648 prefs_common.warn_dnd = FALSE;
2651 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2652 from_folder->name, to_folder->name);
2653 STATUSBAR_PUSH(folderview->mainwin, buf);
2655 summary_clear_all(folderview->summaryview);
2656 folderview->opened = NULL;
2657 folderview->selected = NULL;
2658 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2660 main_window_cursor_wait(folderview->mainwin);
2662 statusbar_verbosity_set(FALSE);
2663 folder_item_update_freeze();
2664 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2665 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2666 statusbar_verbosity_set(FALSE);
2667 main_window_cursor_normal(folderview->mainwin);
2668 STATUSBAR_POP(folderview->mainwin);
2669 folder_item_update_thaw();
2670 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2672 folderview_sort_folders(folderview,
2673 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2674 NULL, to_folder), new_folder->folder);
2675 folderview_select(folderview, new_folder);
2676 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2678 statusbar_verbosity_set(FALSE);
2679 main_window_cursor_normal(folderview->mainwin);
2680 STATUSBAR_POP(folderview->mainwin);
2681 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2682 folder_item_update_thaw();
2684 case F_MOVE_FAILED_DEST_IS_PARENT:
2685 alertpanel_error(_("Source and destination are the same."));
2687 case F_MOVE_FAILED_DEST_IS_CHILD:
2688 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2689 _("Can't move a folder to one of its children."));
2691 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2692 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2695 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2700 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2703 static gint folderview_clist_compare(GtkCMCList *clist,
2704 gconstpointer ptr1, gconstpointer ptr2)
2706 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2707 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2709 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2711 return item1->order - item2->order;
2714 // if only one folder has an order it comes first
2715 if (item1->order > 0)
2719 if (item2->order > 0)
2725 return (item2->name != NULL);
2729 return g_utf8_collate(item1->name, item2->name);
2732 static void folderview_processing_cb(GtkAction *action, gpointer data)
2734 FolderView *folderview = (FolderView *)data;
2738 if (!folderview->selected) return;
2740 item = folderview_get_selected_item(folderview);
2741 cm_return_if_fail(item != NULL);
2742 cm_return_if_fail(item->folder != NULL);
2744 id = folder_item_get_identifier(item);
2745 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2748 prefs_filtering_open(&item->prefs->processing, title,
2749 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2753 void folderview_set_target_folder_color(gint color_op)
2757 FolderView *folderview;
2759 for (list = folderview_list; list != NULL; list = list->next) {
2760 folderview = (FolderView *)list->data;
2761 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2763 bold_tgtfold_style->text[GTK_STATE_NORMAL] =
2764 folderview->color_op;
2770 static gchar *last_smallfont = NULL;
2771 static gchar *last_normalfont = NULL;
2772 static gchar *last_boldfont = NULL;
2773 static gboolean last_derive = 0;
2775 void folderview_reinit_fonts(FolderView *folderview)
2778 g_free(last_smallfont);
2779 last_smallfont = NULL;
2780 g_free(last_normalfont);
2781 last_normalfont = NULL;
2782 g_free(last_boldfont);
2783 last_boldfont = NULL;
2786 void folderview_reflect_prefs(void)
2788 gboolean update_font = FALSE;
2789 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2790 FolderItem *item = folderview_get_selected_item(folderview);
2791 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2792 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2793 gint height = gtk_adjustment_get_value(pos);
2795 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2796 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2797 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2798 last_derive != prefs_common.derive_from_normal_font)
2804 g_free(last_smallfont);
2805 last_smallfont = g_strdup(SMALL_FONT);
2806 g_free(last_normalfont);
2807 last_normalfont = g_strdup(NORMAL_FONT);
2808 g_free(last_boldfont);
2809 last_boldfont = g_strdup(BOLD_FONT);
2810 last_derive = prefs_common.derive_from_normal_font;
2812 #define STYLE_FREE(s) \
2814 g_object_unref(s); \
2818 STYLE_FREE(normal_style);
2819 STYLE_FREE(normal_color_style);
2820 STYLE_FREE(bold_style);
2821 STYLE_FREE(bold_color_style);
2822 STYLE_FREE(bold_tgtfold_style);
2826 folderview_init(folderview);
2827 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2828 folderview_column_set_titles(folderview);
2829 folderview_set_all();
2831 g_signal_handlers_block_by_func
2832 (G_OBJECT(folderview->ctree),
2833 G_CALLBACK(folderview_selected), folderview);
2836 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2837 GTK_CMCTREE(folderview->ctree), NULL, item);
2839 folderview_select(folderview, item);
2840 folderview->open_folder = FALSE;
2841 folderview->selected = node;
2844 g_signal_handlers_unblock_by_func
2845 (G_OBJECT(folderview->ctree),
2846 G_CALLBACK(folderview_selected), folderview);
2848 pos = gtk_scrolled_window_get_vadjustment(
2849 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2850 gtk_adjustment_set_value(pos, height);
2851 gtk_adjustment_changed(pos);
2852 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2855 static void drag_state_stop(FolderView *folderview)
2857 if (folderview->drag_timer_id)
2858 g_source_remove(folderview->drag_timer_id);
2859 folderview->drag_timer_id = 0;
2860 folderview->drag_node = NULL;
2863 static gboolean folderview_defer_expand(FolderView *folderview)
2865 if (folderview->drag_node) {
2866 folderview_recollapse_nodes(folderview, folderview->drag_node);
2867 if (folderview->drag_item->collapsed) {
2868 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2869 folderview->nodes_to_recollapse = g_slist_append
2870 (folderview->nodes_to_recollapse, folderview->drag_node);
2873 folderview->drag_item = NULL;
2874 folderview->drag_timer_id = 0;
2878 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2880 /* the idea is that we call drag_state_start() whenever we want expansion to
2881 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2882 * we need to call drag_state_stop() */
2883 drag_state_stop(folderview);
2884 /* request expansion */
2885 if (0 != (folderview->drag_timer_id = g_timeout_add
2886 (prefs_common.hover_timeout,
2887 (GSourceFunc)folderview_defer_expand,
2889 folderview->drag_node = node;
2890 folderview->drag_item = item;
2893 #ifndef GENERIC_UMPC
2894 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2895 FolderView *folderview)
2897 GdkDragContext *context;
2899 cm_return_if_fail(folderview != NULL);
2900 if (folderview->selected == NULL) return;
2901 if (folderview->nodes_to_recollapse)
2902 g_slist_free(folderview->nodes_to_recollapse);
2903 folderview->nodes_to_recollapse = NULL;
2904 context = gtk_drag_begin(widget, folderview->target_list,
2905 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2906 gtk_drag_set_icon_default(context);
2909 static void folderview_drag_data_get(GtkWidget *widget,
2910 GdkDragContext *drag_context,
2911 GtkSelectionData *selection_data,
2914 FolderView *folderview)
2918 gchar *source = NULL;
2919 if (info == TARGET_DUMMY) {
2920 sel = GTK_CMCLIST(folderview->ctree)->selection;
2924 item = gtk_cmctree_node_get_row_data
2925 (GTK_CMCTREE(folderview->ctree),
2926 GTK_CMCTREE_NODE(sel->data));
2928 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2929 gtk_selection_data_set(selection_data,
2930 gtk_selection_data_get_target(selection_data), 8,
2931 source, strlen(source));
2934 g_warning("unknown info %d", info);
2938 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2940 FolderUpdateData *hookdata;
2941 FolderView *folderview;
2945 folderview = (FolderView *) userdata;
2946 cm_return_val_if_fail(hookdata != NULL, FALSE);
2947 cm_return_val_if_fail(folderview != NULL, FALSE);
2949 ctree = folderview->ctree;
2950 cm_return_val_if_fail(ctree != NULL, FALSE);
2952 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2953 folderview_create_folder_node(folderview, hookdata->item);
2954 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2955 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2956 NULL, folder_item_parent(hookdata->item));
2957 folderview_sort_folders(folderview, node, hookdata->folder);
2958 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2959 GtkCMCTreeNode *node;
2961 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2963 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2964 if (folderview->selected == node)
2965 folderview->selected = NULL;
2966 if (folderview->opened == node)
2967 folderview->opened = NULL;
2969 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2970 /* do nothing, it's done by the ADD and REMOVE) */
2971 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2972 folderview_set(folderview);
2977 static gboolean folderview_dnd_scroll_cb(gpointer data)
2979 FolderView *folderview = (FolderView *)data;
2980 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2981 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2982 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2983 gint max = (int)gtk_adjustment_get_upper(pos) -
2984 (int)gtk_adjustment_get_page_size(pos);
2986 if (folderview->scroll_value == 0) {
2987 folderview->scroll_timeout_id = 0;
2991 if (folderview->scroll_value > 0 && new_val > max) {
2993 } else if (folderview->scroll_value < 0 && new_val < 0) {
2996 gtk_adjustment_set_value(pos, new_val);
3001 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
3002 GdkDragContext *context,
3006 FolderView *folderview)
3009 FolderItem *item = NULL, *src_item = NULL;
3010 GtkCMCTreeNode *node = NULL;
3011 gboolean acceptable = FALSE;
3012 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3013 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3014 int height = (int)gtk_adjustment_get_page_size(pos);
3015 int total_height = (int)gtk_adjustment_get_upper(pos);
3016 int vpos = (int)gtk_adjustment_get_value(pos);
3017 int offset = prefs_common.show_col_headers ? 24:0;
3020 if (gtk_cmclist_get_selection_info
3021 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3022 GtkWidget *srcwidget;
3024 if (y > height - (48 - offset) && height + vpos < total_height) {
3025 dist = -(height - (48 - offset) - y);
3026 folderview->scroll_value = 1.41f * (1+(dist / 6));
3027 } else if (y < 72 - (24 - offset) && y >= 0) {
3028 dist = 72 - (24 - offset) - y;
3029 folderview->scroll_value = -1.41f * (1+(dist / 6));
3031 folderview->scroll_value = 0;
3033 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3034 folderview->scroll_timeout_id =
3035 g_timeout_add(30, folderview_dnd_scroll_cb,
3039 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3040 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3041 src_item = folderview->summaryview->folder_item;
3043 srcwidget = gtk_drag_get_source_widget(context);
3044 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3045 /* comes from summaryview */
3046 /* we are copying messages, so only accept folder items that are not
3047 the source item, are no root items and can copy messages */
3048 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3049 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3050 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3052 } else if (srcwidget == folderview->ctree) {
3053 /* comes from folderview */
3054 /* we are moving folder items, only accept folders that are not
3055 the source items and can copy messages and create folder items */
3056 if (item && item->folder && src_item && src_item != item &&
3057 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3058 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3059 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3060 || item->folder == src_item->folder))
3063 /* comes from another app */
3064 /* we are adding messages, so only accept folder items that are
3065 no root items and can copy messages */
3066 if (item && item->folder && folder_item_parent(item) != NULL
3067 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3068 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3073 if (acceptable || (src_item && src_item == item))
3074 drag_state_start(folderview, node, item);
3077 g_signal_handlers_block_by_func
3079 G_CALLBACK(folderview_selected), folderview);
3080 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3081 g_signal_handlers_unblock_by_func
3083 G_CALLBACK(folderview_selected), folderview);
3084 gdk_drag_status(context,
3085 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3086 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3088 if (folderview->opened)
3089 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3090 gdk_drag_status(context, 0, time);
3096 static void folderview_drag_leave_cb(GtkWidget *widget,
3097 GdkDragContext *context,
3099 FolderView *folderview)
3101 drag_state_stop(folderview);
3102 folderview->scroll_value = 0;
3103 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3106 static void free_info (gpointer stuff, gpointer data)
3111 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3112 guint time, FolderItem *item)
3115 GSList *msglist = NULL;
3116 list = uri_list_extract_filenames(data);
3117 if (!(item && item->folder && folder_item_parent(item) != NULL
3118 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3120 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3121 debug_print("item doesn't fit\n");
3125 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3126 debug_print("list is empty\n");
3129 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3130 MsgFileInfo *info = NULL;
3132 if (file_is_email((gchar *)tmp->data)) {
3133 info = g_new0(MsgFileInfo, 1);
3134 info->msginfo = NULL;
3135 info->file = (gchar *)tmp->data;
3136 msglist = g_slist_prepend(msglist, info);
3137 debug_print("file is a mail\n");
3139 debug_print("file isn't a mail\n");
3143 msglist = g_slist_reverse(msglist);
3144 folder_item_add_msgs(item, msglist, FALSE);
3145 g_slist_foreach(msglist, free_info, NULL);
3146 g_slist_free(msglist);
3147 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3149 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3151 list_free_strings_full(list);
3154 static void folderview_drag_received_cb(GtkWidget *widget,
3155 GdkDragContext *drag_context,
3158 GtkSelectionData *data,
3161 FolderView *folderview)
3164 FolderItem *item = NULL, *src_item;
3165 GtkCMCTreeNode *node;
3166 int offset = prefs_common.show_col_headers ? 24:0;
3168 folderview->scroll_value = 0;
3170 if (info == TARGET_DUMMY) {
3171 drag_state_stop(folderview);
3172 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3173 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3174 /* comes from summaryview */
3175 if (gtk_cmclist_get_selection_info
3176 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3179 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3180 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3181 src_item = folderview->summaryview->folder_item;
3183 if (item->no_select) {
3184 alertpanel_error(_("The destination folder can only be used to "
3185 "store subfolders."));
3188 /* re-check (due to acceptable possibly set for folder moves */
3189 if (!(item && item->folder && item->path && !item->no_select &&
3190 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3194 switch (gdk_drag_context_get_selected_action(drag_context)) {
3195 case GDK_ACTION_COPY:
3196 summary_copy_selected_to(folderview->summaryview, item);
3197 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3199 case GDK_ACTION_MOVE:
3200 case GDK_ACTION_DEFAULT:
3202 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3203 summary_copy_selected_to(folderview->summaryview, item);
3205 summary_move_selected_to(folderview->summaryview, item);
3206 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3209 /* comes from folderview */
3211 gboolean folder_is_normal = TRUE;
3212 gboolean copy = (GDK_ACTION_COPY ==
3213 gdk_drag_context_get_selected_action(drag_context));
3215 source = (char *)gtk_selection_data_get_data(data) + 17;
3216 if (gtk_cmclist_get_selection_info
3217 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3219 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3222 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3223 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3224 src_item = folder_find_item_from_identifier(source);
3228 src_item->stype == F_NORMAL &&
3229 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3230 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3231 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3232 !folder_has_parent_of_type(src_item, F_TRASH);
3233 if (!item || !src_item || !folder_is_normal) {
3234 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3238 folderview_move_folder(folderview, src_item, item, copy);
3239 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3241 folderview->nodes_to_recollapse = NULL;
3242 } else if (info == TARGET_MAIL_URI_LIST) {
3243 if (gtk_cmclist_get_selection_info
3244 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3247 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3249 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3250 debug_print("no node\n");
3253 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3255 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3256 debug_print("no item\n");
3259 folderview_finish_dnd(gtk_selection_data_get_data(data),
3260 drag_context, time, item);
3264 static void folderview_drag_end_cb(GtkWidget *widget,
3265 GdkDragContext *drag_context,
3266 FolderView *folderview)
3268 drag_state_stop(folderview);
3269 folderview->scroll_value = 0;
3270 g_slist_free(folderview->nodes_to_recollapse);
3271 folderview->nodes_to_recollapse = NULL;
3274 void folderview_register_popup(FolderViewPopup *fpopup)
3278 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3279 FolderView *folderview = folderviews->data;
3280 GtkActionGroup *factory;
3282 factory = create_action_group(folderview, fpopup);
3283 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3285 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3288 void folderview_unregister_popup(FolderViewPopup *fpopup)
3293 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3294 FolderView *folderview = folderviews->data;
3296 g_hash_table_remove(folderview->popups, fpopup->klass);
3298 g_hash_table_remove(folderview_popups, fpopup->klass);
3301 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3303 g_return_if_fail(folderview != NULL);
3304 g_return_if_fail(item != NULL);
3306 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3307 g_return_if_fail(ctree != NULL);
3309 GtkCMCTreeNode *node =
3310 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3311 g_return_if_fail(node != NULL);
3313 gtk_cmctree_remove_node(ctree, node);
3316 void folderview_freeze(FolderView *folderview)
3319 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3322 void folderview_thaw(FolderView *folderview)
3325 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3328 void folderview_grab_focus(FolderView *folderview)
3331 gtk_widget_grab_focus(folderview->ctree);