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(void)
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;
1523 item = gtk_cmctree_node_get_row_data(ctree, node);
1524 cm_return_if_fail(item != NULL);
1526 if (!GTK_CMCTREE_ROW(node)->expanded)
1527 mark = folderview_have_marked_children(folderview, item);
1529 mark = (item->marked_msgs != 0);
1531 stype = item->stype;
1532 if (stype == F_NORMAL) {
1533 if (folder_has_parent_of_type(item, F_TRASH))
1535 else if (folder_has_parent_of_type(item, F_DRAFT))
1537 else if (folder_has_parent_of_type(item, F_OUTBOX))
1539 else if (folder_has_parent_of_type(item, F_QUEUE))
1544 if (item->hide_read_msgs || item->hide_read_threads) {
1545 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1546 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1548 xpm = mark?m_inboxxpm:inboxxpm;
1549 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1553 if (item->hide_read_msgs || item->hide_read_threads) {
1554 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1555 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1557 xpm = mark?m_outboxxpm:outboxxpm;
1558 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1562 if (item->hide_read_msgs || item->hide_read_threads) {
1563 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1564 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1566 xpm = mark?m_queuexpm:queuexpm;
1567 openxpm = mark?m_queueopenxpm:queueopenxpm;
1571 if (item->hide_read_msgs || item->hide_read_threads) {
1572 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1573 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1575 xpm = mark?m_trashxpm:trashxpm;
1576 openxpm = mark?m_trashopenxpm:trashopenxpm;
1580 xpm = mark?m_draftsxpm:draftsxpm;
1581 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1585 FOLDER_TYPE(item->folder) == F_IMAP &&
1586 item->folder->account->imap_subsonly) {
1587 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1588 openxpm = foldersubsopenxpm;
1589 } else if (item->no_select) {
1590 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1591 openxpm = foldernoselectopenxpm;
1592 } else if (item->hide_read_msgs || item->hide_read_threads) {
1593 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1594 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1596 xpm = mark?m_folderxpm:folderxpm;
1597 openxpm = mark?m_folderopenxpm:folderopenxpm;
1601 name = folder_item_get_name(item);
1603 if (!GTK_CMCTREE_ROW(node)->expanded) {
1604 add_unread_mark = folderview_have_unread_children(
1606 add_sub_match_mark = folderview_have_matching_children(
1609 add_unread_mark = FALSE;
1610 add_sub_match_mark = FALSE;
1613 if (item->search_match) {
1615 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1618 xpm = openxpm = searchicon;
1622 if (prefs_common.display_folder_unread) {
1623 if (folder_has_parent_of_type(item, F_QUEUE)) {
1624 /* only total_msgs matters here */
1625 if (item->total_msgs > 0) {
1626 /* show total number (should be equal to the unread number)
1628 str = g_strdup_printf("%s (%d%s%s)",
1629 name, item->total_msgs,
1630 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1631 (item->unreadmarked_msgs > 0) ? "!" : "");
1634 if (prefs_common.display_folder_unread == 1) {
1635 if (item->unread_msgs > 0) {
1636 /* show unread number and signs */
1637 str = g_strdup_printf("%s (%d%s%s)",
1638 name, item->unread_msgs,
1639 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1640 (item->unreadmarked_msgs > 0) ? "!" : "");
1643 if (item->total_msgs > 0) {
1644 /* show unread number, total number and signs if any */
1645 str = g_strdup_printf("%s (%d/%d%s%s)",
1646 name, item->unread_msgs, item->total_msgs,
1647 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1648 (item->unreadmarked_msgs > 0) ? "!" : "");
1652 if ((str == NULL) &&
1653 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1654 /* no unread/total numbers, but at least one sign */
1655 str = g_strdup_printf("%s (%s%s)",
1657 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1658 (item->unreadmarked_msgs > 0) ? "!" : "");
1662 /* last fallback, folder name only or with +! sign */
1663 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1664 str = g_strdup_printf("%s%s",
1666 } else if (item->unreadmarked_msgs > 0) {
1667 str = g_strdup_printf("%s%s",
1669 } else if (add_sub_match_mark) {
1670 str = g_strdup_printf("%s%s",
1673 str = g_strdup_printf("%s", name);
1676 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1678 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1682 if (!folder_item_parent(item)) {
1683 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1684 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1685 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1687 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1689 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1692 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1693 folder_has_parent_of_type(item, F_TRASH)) {
1694 use_bold = use_color = FALSE;
1695 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1696 GSList *list = folder_item_get_msg_list(item);
1698 use_bold = use_color = FALSE;
1699 for (cur = list; cur; cur = cur->next) {
1700 MsgInfo *msginfo = (MsgInfo *)cur->data;
1701 if (!MSG_IS_DELETED(msginfo->flags)) {
1702 /* highlight queue folder if there are any messages */
1703 use_bold = use_color = TRUE;
1707 if (!GTK_CMCTREE_ROW(node)->expanded &&
1708 use_bold == FALSE &&
1709 folderview_have_children(folderview, item))
1710 use_bold = use_color = TRUE;
1711 procmsg_msg_list_free(list);
1713 /* if unread messages exist, print with bold font */
1714 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1716 /* if new messages exist, print with colored letter */
1718 (item->new_msgs > 0) ||
1720 folderview_have_new_children(folderview, item));
1723 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1728 if (item->prefs->color > 0 && !use_color) {
1729 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1730 color_style = gtk_style_copy(bold_style);
1731 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1732 style = color_style;
1733 } else if (use_color) {
1734 style = bold_color_style;
1737 if (item->op_count > 0) {
1738 style = bold_tgtfold_style;
1740 } else if (use_color) {
1741 style = normal_color_style;
1742 gtk_cmctree_node_set_foreground(ctree, node,
1743 &folderview->color_new);
1744 } else if (item->op_count > 0) {
1745 style = bold_tgtfold_style;
1746 } else if (item->prefs->color > 0) {
1748 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1749 color_style = gtk_style_copy(normal_style);
1750 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1751 style = color_style;
1753 style = normal_style;
1756 gtk_cmctree_node_set_row_style(ctree, node, style);
1758 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1759 folderview_update_node(folderview, node);
1762 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1765 FolderView *folderview;
1767 GtkCMCTreeNode *node;
1769 cm_return_if_fail(item != NULL);
1771 for (list = folderview_list; list != NULL; list = list->next) {
1772 folderview = (FolderView *)list->data;
1773 ctree = GTK_CMCTREE(folderview->ctree);
1775 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1776 if (node && item->search_match != matches) {
1777 item->search_match = matches;
1778 folderview_update_node(folderview, node);
1783 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1785 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1786 FolderView *folderview = (FolderView *)data;
1788 GtkCMCTreeNode *node;
1789 cm_return_val_if_fail(update_info != NULL, TRUE);
1790 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1791 cm_return_val_if_fail(folderview != NULL, FALSE);
1793 ctree = GTK_CMCTREE(folderview->ctree);
1795 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1798 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1799 folderview_update_node(folderview, node);
1801 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1802 update_info->item == folderview->summaryview->folder_item &&
1803 update_info->item != NULL)
1804 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1805 summary_show(folderview->summaryview, update_info->item);
1811 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1812 GNode *gnode, GtkCMCTreeNode *cnode,
1815 FolderView *folderview = (FolderView *)data;
1816 FolderItem *item = FOLDER_ITEM(gnode->data);
1818 cm_return_val_if_fail(item != NULL, FALSE);
1820 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1821 folderview_update_node(folderview, cnode);
1826 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1829 FolderView *folderview = (FolderView *)data;
1832 if (GTK_CMCTREE_ROW(node)->children) {
1833 item = gtk_cmctree_node_get_row_data(ctree, node);
1834 cm_return_if_fail(item != NULL);
1836 if (!item->collapsed)
1837 gtk_cmctree_expand(ctree, node);
1839 folderview_update_node(folderview, node);
1843 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1844 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1847 GtkCMCTreeNode *node, *parent, *sibling;
1849 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1851 g_warning("%s not found.", item->path);
1853 parent = GTK_CMCTREE_ROW(node)->parent;
1854 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1855 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1857 sibling = GTK_CMCTREE_ROW(parent)->children;
1861 tmp = gtk_cmctree_node_get_row_data
1863 if (tmp && tmp->stype != F_NORMAL)
1864 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1868 if (node != sibling)
1869 gtk_cmctree_move(ctree, node, parent, sibling);
1876 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1879 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1880 GtkCMCTreeNode *prev = NULL;
1882 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1883 gtk_sctree_sort_recursive(ctree, root);
1884 if (root && GTK_CMCTREE_ROW(root)->parent) {
1885 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1888 set_special_folder(ctree, folder->inbox, root, &prev);
1889 set_special_folder(ctree, folder->outbox, root, &prev);
1890 set_special_folder(ctree, folder->draft, root, &prev);
1891 set_special_folder(ctree, folder->queue, root, &prev);
1892 set_special_folder(ctree, folder->trash, root, &prev);
1893 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1896 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1898 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1899 GtkCMCTreeNode *root;
1901 cm_return_if_fail(folder != NULL);
1903 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1904 folderview_gnode_func, folderview);
1905 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1907 folderview_sort_folders(folderview, root, folder);
1910 /* callback functions */
1911 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1912 GdkEventButton *event)
1916 FolderViewPopup *fpopup;
1917 GtkActionGroup *action_group;
1919 FolderItem *special_trash = NULL, *special_queue = NULL;
1921 GtkUIManager *ui_manager = gtk_ui_manager_new();
1923 if (folderview->ui_manager)
1924 g_object_unref(folderview->ui_manager);
1926 folderview->ui_manager = ui_manager;
1927 item = folderview_get_selected_item(folderview);
1929 cm_return_if_fail(item != NULL);
1930 cm_return_if_fail(item->folder != NULL);
1931 folder = item->folder;
1933 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1936 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1938 fpopup = g_hash_table_lookup(folderview_popups, "common");
1939 action_group = g_hash_table_lookup(folderview->popups, "common");
1942 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1943 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1944 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1946 if (fpopup->add_menuitems)
1947 fpopup->add_menuitems(ui_manager, item);
1949 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1950 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1959 if (fpopup->set_sensitivity != NULL)
1960 fpopup->set_sensitivity(ui_manager, item);
1962 if (NULL != (ac = account_find_from_item(item))) {
1963 special_trash = account_get_special_folder(ac, F_TRASH);
1964 special_queue = account_get_special_folder(ac, F_QUEUE);
1967 if ((item == folder->trash || item == special_trash
1968 || folder_has_parent_of_type(item, F_TRASH))) {
1969 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1970 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1973 if ((item == folder->queue || item == special_queue
1974 || folder_has_parent_of_type(item, F_QUEUE))) {
1975 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1979 #define SET_SENS(name, sens) \
1980 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1982 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1983 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1984 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1985 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1986 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1987 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1988 folderview->selected == folderview->opened);
1989 SET_SENS("FolderViewPopup/Properties", TRUE);
1991 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1992 item->total_msgs >= 1 && !item->processing_pending);
1993 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1994 !item->no_select && !item->processing_pending);
1996 if (item == folder->trash || item == special_trash
1997 || folder_has_parent_of_type(item, F_TRASH)) {
1998 GSList *msglist = folder_item_get_msg_list(item);
1999 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2000 procmsg_msg_list_free(msglist);
2002 if (item == folder->queue || item == special_queue
2003 || folder_has_parent_of_type(item, F_QUEUE)) {
2004 GSList *msglist = folder_item_get_msg_list(item);
2005 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2006 procmsg_msg_list_free(msglist);
2010 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2011 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2012 g_signal_connect(G_OBJECT(popup), "selection_done",
2013 G_CALLBACK(folderview_popup_close),
2015 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2016 event->button, event->time);
2019 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2020 FolderView *folderview)
2022 GtkCMCList *clist = GTK_CMCLIST(ctree);
2023 gint prev_row = -1, row = -1, column = -1;
2025 if (!event) return FALSE;
2026 if (event->window != clist->clist_window) return FALSE;
2028 if (event->button == 1 || event->button == 2) {
2029 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2030 folderview->open_folder = TRUE;
2032 if (event->type == GDK_2BUTTON_PRESS) {
2033 if (clist->selection) {
2034 GtkCMCTreeNode *node;
2036 node = GTK_CMCTREE_NODE(clist->selection->data);
2038 gtk_cmctree_toggle_expansion(
2041 folderview->open_folder = FALSE;
2048 if (event->button == 2 || event->button == 3) {
2050 if (clist->selection) {
2051 GtkCMCTreeNode *node;
2053 node = GTK_CMCTREE_NODE(clist->selection->data);
2055 prev_row = gtkut_ctree_get_nth_from_node
2056 (GTK_CMCTREE(ctree), node);
2059 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2062 if (prev_row != row) {
2063 gtk_cmclist_unselect_all(clist);
2064 if (event->button == 2)
2065 folderview_select_node
2067 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2070 gtk_cmclist_select_row(clist, row, column);
2074 if (event->button != 3) return FALSE;
2076 folderview_set_sens_and_popup_menu(folderview, row, event);
2080 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2081 FolderView *folderview)
2083 int row = -1, column = -1;
2085 if (!event) return FALSE;
2087 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2090 if (event->button == 1 && folderview->open_folder == FALSE &&
2091 folderview->opened != NULL) {
2092 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2093 folderview->opened);
2094 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2100 #define BREAK_ON_MODIFIER_KEY() \
2101 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2103 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2104 FolderView *folderview)
2106 GtkCMCTreeNode *node;
2109 if (!event) return FALSE;
2111 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2114 switch (event->keyval) {
2116 if (folderview->selected) {
2117 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2118 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2119 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2120 folderview->selected);
2122 folderview_select_node(folderview,
2123 folderview->selected);
2127 case GDK_KEY_Return:
2128 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2129 gtk_cmctree_toggle_expansion(
2130 GTK_CMCTREE(folderview->ctree),
2131 folderview->selected);
2135 case GDK_KEY_Return:
2136 case GDK_KEY_KP_Enter:
2137 if (folderview->selected)
2138 folderview_select_node(folderview, folderview->selected);
2142 BREAK_ON_MODIFIER_KEY();
2143 if (folderview->selected) {
2144 if (folderview->opened == folderview->selected &&
2145 (!folderview->summaryview->folder_item ||
2146 folderview->summaryview->folder_item->total_msgs == 0))
2147 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2149 folderview_select_node(folderview,
2150 folderview->selected);
2154 if (folderview->selected) {
2155 /* If the folder is expanded and can be collapsed, do that... */
2156 if (GTK_CMCTREE_ROW(folderview->selected)->expanded &&
2157 GTK_CMCTREE_ROW(folderview->selected)->children != NULL) {
2158 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2159 folderview->selected);
2161 /* ...otherwise, move cursor to its parent node. */
2162 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2163 folderview->selected))) {
2164 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2165 NULL, folder_item_parent(item)))) {
2166 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2167 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2168 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2177 if (event->keyval == GDK_KEY_Home)
2178 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2180 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2181 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2183 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2185 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2186 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2196 typedef struct _PostponedSelectData
2199 GtkCMCTreeNode *row;
2201 FolderView *folderview;
2202 } PostponedSelectData;
2204 static gboolean postpone_select(void *data)
2206 PostponedSelectData *psdata = (PostponedSelectData *)data;
2207 debug_print("trying again\n");
2209 psdata->folderview->postpone_select_id = 0;
2210 psdata->folderview->open_folder = TRUE;
2211 main_window_cursor_normal(psdata->folderview->mainwin);
2212 STATUSBAR_POP(psdata->folderview->mainwin);
2213 folderview_selected(psdata->ctree, psdata->row,
2214 psdata->column, psdata->folderview);
2219 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2221 if (folderview->opened) {
2223 folderview->opened = NULL;
2227 FolderItem *olditem =
2228 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2229 folderview->opened);
2231 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2232 olditem->path ? olditem->path:olditem->name);
2233 /* will be null if we just moved the previously opened folder */
2234 STATUSBAR_PUSH(folderview->mainwin, buf);
2235 main_window_cursor_wait(folderview->mainwin);
2237 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2238 summary_show(folderview->summaryview, NULL);
2239 folder_item_close(olditem);
2240 main_window_cursor_normal(folderview->mainwin);
2241 STATUSBAR_POP(folderview->mainwin);
2242 if (olditem->folder->klass->item_closed)
2243 olditem->folder->klass->item_closed(olditem);
2248 if (folderview->opened &&
2249 !GTK_CMCTREE_ROW(folderview->opened)->children)
2250 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2252 folderview->opened = NULL;
2254 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2255 gint column, FolderView *folderview)
2257 static gboolean can_select = TRUE; /* exclusive lock */
2262 GtkCMCTreeNode *old_opened = folderview->opened;
2264 folderview->selected = row;
2266 debug_print("newly selected %p, opened %p\n", folderview->selected,
2267 folderview->opened);
2268 if (folderview->opened == row) {
2269 folderview->open_folder = FALSE;
2274 item = gtk_cmctree_node_get_row_data(ctree, row);
2277 folderview->open_folder = FALSE;
2281 if (!can_select || summary_is_locked(folderview->summaryview)) {
2282 if (folderview->opened) {
2283 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2284 gtk_cmctree_select(ctree, folderview->opened);
2286 folderview->open_folder = FALSE;
2291 if (!folderview->open_folder) {
2298 /* Save cache for old folder */
2299 /* We don't want to lose all caches if app crashes */
2300 /* Resets folderview->opened to NULL */
2301 folderview_close_opened(folderview, FALSE);
2303 /* CLAWS: set compose button type: news folder items
2304 * always have a news folder as parent */
2306 toolbar_set_compose_button
2307 (folderview->mainwin->toolbar,
2308 FOLDER_TYPE(item->folder) == F_NEWS ?
2309 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2312 debug_print("Folder %s is selected\n", item->path);
2314 if (!GTK_CMCTREE_ROW(row)->children)
2315 gtk_cmctree_expand(ctree, row);
2317 /* ungrab the mouse event */
2318 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2319 gtk_grab_remove(GTK_WIDGET(ctree));
2320 if (gdk_pointer_is_grabbed())
2321 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2325 /* TODO: wwp: avoid displaying (null) in the status bar */
2326 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2327 item->path : "(null)");
2328 debug_print("%s\n", buf);
2329 STATUSBAR_PUSH(folderview->mainwin, buf);
2332 main_window_cursor_wait(folderview->mainwin);
2334 if (folderview->scanning_folder == item->folder) {
2337 res = folder_item_open(item);
2340 if (res == -1 && item->no_select == FALSE) {
2341 main_window_cursor_normal(folderview->mainwin);
2342 STATUSBAR_POP(folderview->mainwin);
2344 alertpanel_error(_("Folder could not be opened."));
2346 folderview->open_folder = FALSE;
2350 } else if (res == -2 && item->no_select == FALSE) {
2351 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2352 data->ctree = ctree;
2354 data->column = column;
2355 data->folderview = folderview;
2356 debug_print("postponing open of %s till end of scan\n",
2357 item->path ? item->path:item->name);
2358 folderview->open_folder = FALSE;
2360 if (folderview->postpone_select_id != 0)
2361 g_source_remove(folderview->postpone_select_id);
2362 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2367 main_window_cursor_normal(folderview->mainwin);
2370 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2371 opened = summary_show(folderview->summaryview, item);
2373 folder_clean_cache_memory(item);
2376 gtkut_ctree_set_focus_row(ctree, old_opened);
2377 gtk_cmctree_select(ctree, old_opened);
2378 folderview->opened = old_opened;
2380 folderview->opened = row;
2381 if (gtk_cmctree_node_is_visible(ctree, row)
2382 != GTK_VISIBILITY_FULL)
2383 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2386 STATUSBAR_POP(folderview->mainwin);
2388 folderview->open_folder = FALSE;
2393 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2394 FolderView *folderview)
2398 item = gtk_cmctree_node_get_row_data(ctree, node);
2399 cm_return_if_fail(item != NULL);
2400 item->collapsed = FALSE;
2401 folderview_update_node(folderview, node);
2404 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2405 FolderView *folderview)
2409 item = gtk_cmctree_node_get_row_data(ctree, node);
2410 cm_return_if_fail(item != NULL);
2411 item->collapsed = TRUE;
2412 folderview_update_node(folderview, node);
2415 static void folderview_popup_close(GtkMenuShell *menu_shell,
2416 FolderView *folderview)
2418 if (!folderview->opened) return;
2420 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2423 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2424 FolderView *folderview)
2426 FolderColumnType type = folderview->col_state[column].type;
2428 prefs_common.folder_col_size[type] = width;
2431 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2433 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2434 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2435 GtkCMCTreeNode *node, *parent_node;
2436 gint *col_pos = folderview->col_pos;
2437 FolderItemUpdateData hookdata;
2439 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2440 if (parent_node == NULL)
2443 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2445 text[col_pos[F_COL_FOLDER]] = item->name;
2446 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2451 gtk_cmctree_expand(ctree, parent_node);
2452 gtk_cmctree_node_set_row_data(ctree, node, item);
2454 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2455 folderview_sort_folders(folderview, parent_node, item->folder);
2457 hookdata.item = item;
2458 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2459 hookdata.msg = NULL;
2460 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2462 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2465 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2467 FolderView *folderview = (FolderView *)data;
2469 GSList *mlist = NULL;
2471 FolderItem *special_trash = NULL;
2474 if (!folderview->selected) return;
2475 item = folderview_get_selected_item(folderview);
2476 cm_return_if_fail(item != NULL);
2477 cm_return_if_fail(item->folder != NULL);
2479 if (NULL != (ac = account_find_from_item(item)))
2480 special_trash = account_get_special_folder(ac, F_TRASH);
2482 if (item != item->folder->trash && item != special_trash
2483 && !folder_has_parent_of_type(item, F_TRASH)) return;
2485 if (prefs_common.ask_on_clean) {
2486 if (alertpanel(_("Empty trash"),
2487 _("Delete all messages in trash?"),
2488 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2489 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2493 mlist = folder_item_get_msg_list(item);
2495 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2496 MsgInfo * msginfo = (MsgInfo *) cur->data;
2497 if (MSG_IS_LOCKED(msginfo->flags))
2499 /* is it partially received? (partial_recv isn't cached) */
2500 if (msginfo->total_size != 0 &&
2501 msginfo->size != (off_t)msginfo->total_size)
2502 partial_mark_for_delete(msginfo);
2504 procmsg_msg_list_free(mlist);
2506 folder_item_remove_all_msg(item);
2509 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2511 FolderView *folderview = (FolderView *)data;
2513 FolderItem *special_queue = NULL;
2515 gchar *errstr = NULL;
2517 if (!folderview->selected) return;
2518 item = folderview_get_selected_item(folderview);
2519 cm_return_if_fail(item != NULL);
2520 cm_return_if_fail(item->folder != NULL);
2522 if (NULL != (ac = account_find_from_item(item)))
2523 special_queue = account_get_special_folder(ac, F_QUEUE);
2525 if (item != item->folder->queue && item != special_queue
2526 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2528 if (procmsg_queue_is_empty(item))
2531 if (prefs_common.work_offline)
2532 if (alertpanel(_("Offline warning"),
2533 _("You're working offline. Override?"),
2534 GTK_STOCK_NO, GTK_STOCK_YES,
2535 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2538 /* ask for confirmation before sending queued messages only
2539 in online mode and if there is at least one message queued
2540 in any of the folder queue
2542 if (prefs_common.confirm_send_queued_messages) {
2543 if (!prefs_common.work_offline) {
2544 if (alertpanel(_("Send queued messages"),
2545 _("Send all queued messages?"),
2546 GTK_STOCK_CANCEL, _("_Send"),
2547 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2552 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2554 alertpanel_error_log(_("Some errors occurred while "
2555 "sending queued messages."));
2557 alertpanel_error_log(_("Some errors occurred "
2558 "while sending queued messages:\n%s"), errstr);
2564 static void folderview_search_cb(GtkAction *action, gpointer data)
2566 FolderView *folderview = (FolderView *)data;
2567 summary_search(folderview->summaryview);
2570 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2572 FolderView *folderview = (FolderView *)data;
2575 if (!folderview->selected) return;
2577 item = folderview_get_selected_item(folderview);
2578 cm_return_if_fail(item != NULL);
2579 cm_return_if_fail(item->folder != NULL);
2581 item->processing_pending = TRUE;
2582 folder_item_apply_processing(item);
2583 item->processing_pending = FALSE;
2586 static void folderview_property_cb(GtkAction *action, gpointer data)
2588 FolderView *folderview = (FolderView *)data;
2591 if (!folderview->selected) return;
2593 item = folderview_get_selected_item(folderview);
2594 cm_return_if_fail(item != NULL);
2595 cm_return_if_fail(item->folder != NULL);
2597 prefs_folder_item_open(item);
2600 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2602 GSList *list = NULL;
2603 GSList *done = NULL;
2604 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2606 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2607 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2608 && list->data != node) {
2609 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2610 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2613 for (list = done; list != NULL; list = g_slist_next(list)) {
2614 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2620 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2621 FolderItem *to_folder, gboolean copy)
2623 FolderItem *new_folder = NULL;
2627 cm_return_if_fail(folderview != NULL);
2628 cm_return_if_fail(from_folder != NULL);
2629 cm_return_if_fail(to_folder != NULL);
2631 if (prefs_common.warn_dnd) {
2632 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2633 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2634 from_folder->name, to_folder->name);
2635 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2636 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2637 TRUE, NULL, ALERT_QUESTION);
2640 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2642 else if (status & G_ALERTDISABLE)
2643 prefs_common.warn_dnd = FALSE;
2646 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2647 from_folder->name, to_folder->name);
2648 STATUSBAR_PUSH(folderview->mainwin, buf);
2650 summary_clear_all(folderview->summaryview);
2651 folderview->opened = NULL;
2652 folderview->selected = NULL;
2653 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2655 main_window_cursor_wait(folderview->mainwin);
2657 statusbar_verbosity_set(FALSE);
2658 folder_item_update_freeze();
2659 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2660 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2661 statusbar_verbosity_set(FALSE);
2662 main_window_cursor_normal(folderview->mainwin);
2663 STATUSBAR_POP(folderview->mainwin);
2664 folder_item_update_thaw();
2665 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2667 folderview_sort_folders(folderview,
2668 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2669 NULL, to_folder), new_folder->folder);
2670 folderview_select(folderview, new_folder);
2671 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2673 statusbar_verbosity_set(FALSE);
2674 main_window_cursor_normal(folderview->mainwin);
2675 STATUSBAR_POP(folderview->mainwin);
2676 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2677 folder_item_update_thaw();
2679 case F_MOVE_FAILED_DEST_IS_PARENT:
2680 alertpanel_error(_("Source and destination are the same."));
2682 case F_MOVE_FAILED_DEST_IS_CHILD:
2683 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2684 _("Can't move a folder to one of its children."));
2686 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2687 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2690 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2695 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2698 static gint folderview_clist_compare(GtkCMCList *clist,
2699 gconstpointer ptr1, gconstpointer ptr2)
2701 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2702 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2704 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2706 return item1->order - item2->order;
2709 // if only one folder has an order it comes first
2710 if (item1->order > 0)
2714 if (item2->order > 0)
2720 return (item2->name != NULL);
2724 return g_utf8_collate(item1->name, item2->name);
2727 static void folderview_processing_cb(GtkAction *action, gpointer data)
2729 FolderView *folderview = (FolderView *)data;
2733 if (!folderview->selected) return;
2735 item = folderview_get_selected_item(folderview);
2736 cm_return_if_fail(item != NULL);
2737 cm_return_if_fail(item->folder != NULL);
2739 id = folder_item_get_identifier(item);
2740 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2743 prefs_filtering_open(&item->prefs->processing, title,
2744 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2748 void folderview_set_target_folder_color(gint color_op)
2752 FolderView *folderview;
2754 for (list = folderview_list; list != NULL; list = list->next) {
2755 folderview = (FolderView *)list->data;
2756 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2758 bold_tgtfold_style->text[GTK_STATE_NORMAL] =
2759 folderview->color_op;
2765 static gchar *last_smallfont = NULL;
2766 static gchar *last_normalfont = NULL;
2767 static gchar *last_boldfont = NULL;
2768 static gboolean last_derive = 0;
2770 void folderview_reinit_fonts(FolderView *folderview)
2773 g_free(last_smallfont);
2774 last_smallfont = NULL;
2775 g_free(last_normalfont);
2776 last_normalfont = NULL;
2777 g_free(last_boldfont);
2778 last_boldfont = NULL;
2781 void folderview_reflect_prefs(void)
2783 gboolean update_font = FALSE;
2784 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2785 FolderItem *item = folderview_get_selected_item(folderview);
2786 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2787 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2788 gint height = gtk_adjustment_get_value(pos);
2790 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2791 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2792 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2793 last_derive != prefs_common.derive_from_normal_font)
2799 g_free(last_smallfont);
2800 last_smallfont = g_strdup(SMALL_FONT);
2801 g_free(last_normalfont);
2802 last_normalfont = g_strdup(NORMAL_FONT);
2803 g_free(last_boldfont);
2804 last_boldfont = g_strdup(BOLD_FONT);
2805 last_derive = prefs_common.derive_from_normal_font;
2807 #define STYLE_FREE(s) \
2809 g_object_unref(s); \
2813 STYLE_FREE(normal_style);
2814 STYLE_FREE(normal_color_style);
2815 STYLE_FREE(bold_style);
2816 STYLE_FREE(bold_color_style);
2817 STYLE_FREE(bold_tgtfold_style);
2821 folderview_init(folderview);
2822 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2823 folderview_column_set_titles(folderview);
2824 folderview_set_all();
2826 g_signal_handlers_block_by_func
2827 (G_OBJECT(folderview->ctree),
2828 G_CALLBACK(folderview_selected), folderview);
2831 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2832 GTK_CMCTREE(folderview->ctree), NULL, item);
2834 folderview_select(folderview, item);
2835 folderview->open_folder = FALSE;
2836 folderview->selected = node;
2839 g_signal_handlers_unblock_by_func
2840 (G_OBJECT(folderview->ctree),
2841 G_CALLBACK(folderview_selected), folderview);
2843 pos = gtk_scrolled_window_get_vadjustment(
2844 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2845 gtk_adjustment_set_value(pos, height);
2846 gtk_adjustment_changed(pos);
2847 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2850 static void drag_state_stop(FolderView *folderview)
2852 if (folderview->drag_timer_id)
2853 g_source_remove(folderview->drag_timer_id);
2854 folderview->drag_timer_id = 0;
2855 folderview->drag_node = NULL;
2858 static gboolean folderview_defer_expand(FolderView *folderview)
2860 if (folderview->drag_node) {
2861 folderview_recollapse_nodes(folderview, folderview->drag_node);
2862 if (folderview->drag_item->collapsed) {
2863 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2864 folderview->nodes_to_recollapse = g_slist_append
2865 (folderview->nodes_to_recollapse, folderview->drag_node);
2868 folderview->drag_item = NULL;
2869 folderview->drag_timer_id = 0;
2873 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2875 /* the idea is that we call drag_state_start() whenever we want expansion to
2876 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2877 * we need to call drag_state_stop() */
2878 drag_state_stop(folderview);
2879 /* request expansion */
2880 if (0 != (folderview->drag_timer_id = g_timeout_add
2881 (prefs_common.hover_timeout,
2882 (GSourceFunc)folderview_defer_expand,
2884 folderview->drag_node = node;
2885 folderview->drag_item = item;
2888 #ifndef GENERIC_UMPC
2889 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2890 FolderView *folderview)
2892 GdkDragContext *context;
2894 cm_return_if_fail(folderview != NULL);
2895 if (folderview->selected == NULL) return;
2896 if (folderview->nodes_to_recollapse)
2897 g_slist_free(folderview->nodes_to_recollapse);
2898 folderview->nodes_to_recollapse = NULL;
2899 context = gtk_drag_begin(widget, folderview->target_list,
2900 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2901 gtk_drag_set_icon_default(context);
2904 static void folderview_drag_data_get(GtkWidget *widget,
2905 GdkDragContext *drag_context,
2906 GtkSelectionData *selection_data,
2909 FolderView *folderview)
2913 gchar *source = NULL;
2914 if (info == TARGET_DUMMY) {
2915 sel = GTK_CMCLIST(folderview->ctree)->selection;
2919 item = gtk_cmctree_node_get_row_data
2920 (GTK_CMCTREE(folderview->ctree),
2921 GTK_CMCTREE_NODE(sel->data));
2923 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2924 gtk_selection_data_set(selection_data,
2925 gtk_selection_data_get_target(selection_data), 8,
2926 source, strlen(source));
2929 g_warning("unknown info %d", info);
2933 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2935 FolderUpdateData *hookdata;
2936 FolderView *folderview;
2940 folderview = (FolderView *) userdata;
2941 cm_return_val_if_fail(hookdata != NULL, FALSE);
2942 cm_return_val_if_fail(folderview != NULL, FALSE);
2944 ctree = folderview->ctree;
2945 cm_return_val_if_fail(ctree != NULL, FALSE);
2947 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2948 folderview_create_folder_node(folderview, hookdata->item);
2949 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2950 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2951 NULL, folder_item_parent(hookdata->item));
2952 folderview_sort_folders(folderview, node, hookdata->folder);
2953 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2954 GtkCMCTreeNode *node;
2956 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2958 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2959 if (folderview->selected == node)
2960 folderview->selected = NULL;
2961 if (folderview->opened == node)
2962 folderview->opened = NULL;
2964 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2965 /* do nothing, it's done by the ADD and REMOVE) */
2966 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2967 folderview_set(folderview);
2972 static gboolean folderview_dnd_scroll_cb(gpointer data)
2974 FolderView *folderview = (FolderView *)data;
2975 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2976 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2977 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2978 gint max = (int)gtk_adjustment_get_upper(pos) -
2979 (int)gtk_adjustment_get_page_size(pos);
2981 if (folderview->scroll_value == 0) {
2982 folderview->scroll_timeout_id = 0;
2986 if (folderview->scroll_value > 0 && new_val > max) {
2988 } else if (folderview->scroll_value < 0 && new_val < 0) {
2991 gtk_adjustment_set_value(pos, new_val);
2996 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2997 GdkDragContext *context,
3001 FolderView *folderview)
3004 FolderItem *item = NULL, *src_item = NULL;
3005 GtkCMCTreeNode *node = NULL;
3006 gboolean acceptable = FALSE;
3007 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3008 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3009 int height = (int)gtk_adjustment_get_page_size(pos);
3010 int total_height = (int)gtk_adjustment_get_upper(pos);
3011 int vpos = (int)gtk_adjustment_get_value(pos);
3012 int offset = prefs_common.show_col_headers ? 24:0;
3015 if (gtk_cmclist_get_selection_info
3016 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3017 GtkWidget *srcwidget;
3019 if (y > height - (48 - offset) && height + vpos < total_height) {
3020 dist = -(height - (48 - offset) - y);
3021 folderview->scroll_value = 1.41f * (1+(dist / 6));
3022 } else if (y < 72 - (24 - offset) && y >= 0) {
3023 dist = 72 - (24 - offset) - y;
3024 folderview->scroll_value = -1.41f * (1+(dist / 6));
3026 folderview->scroll_value = 0;
3028 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3029 folderview->scroll_timeout_id =
3030 g_timeout_add(30, folderview_dnd_scroll_cb,
3034 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3035 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3036 src_item = folderview->summaryview->folder_item;
3038 srcwidget = gtk_drag_get_source_widget(context);
3039 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3040 /* comes from summaryview */
3041 /* we are copying messages, so only accept folder items that are not
3042 the source item, are no root items and can copy messages */
3043 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3044 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3045 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3047 } else if (srcwidget == folderview->ctree) {
3048 /* comes from folderview */
3049 /* we are moving folder items, only accept folders that are not
3050 the source items and can copy messages and create folder items */
3051 if (item && item->folder && src_item && src_item != item &&
3052 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3053 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3054 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3055 || item->folder == src_item->folder))
3058 /* comes from another app */
3059 /* we are adding messages, so only accept folder items that are
3060 no root items and can copy messages */
3061 if (item && item->folder && folder_item_parent(item) != NULL
3062 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3063 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3068 if (acceptable || (src_item && src_item == item))
3069 drag_state_start(folderview, node, item);
3072 g_signal_handlers_block_by_func
3074 G_CALLBACK(folderview_selected), folderview);
3075 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3076 g_signal_handlers_unblock_by_func
3078 G_CALLBACK(folderview_selected), folderview);
3079 gdk_drag_status(context,
3080 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3081 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3083 if (folderview->opened)
3084 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3085 gdk_drag_status(context, 0, time);
3091 static void folderview_drag_leave_cb(GtkWidget *widget,
3092 GdkDragContext *context,
3094 FolderView *folderview)
3096 drag_state_stop(folderview);
3097 folderview->scroll_value = 0;
3098 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3101 static void free_info (gpointer stuff, gpointer data)
3106 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3107 guint time, FolderItem *item)
3110 GSList *msglist = NULL;
3111 list = uri_list_extract_filenames(data);
3112 if (!(item && item->folder && folder_item_parent(item) != NULL
3113 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3115 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3116 debug_print("item doesn't fit\n");
3120 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3121 debug_print("list is empty\n");
3124 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3125 MsgFileInfo *info = NULL;
3127 if (file_is_email((gchar *)tmp->data)) {
3128 info = g_new0(MsgFileInfo, 1);
3129 info->msginfo = NULL;
3130 info->file = (gchar *)tmp->data;
3131 msglist = g_slist_prepend(msglist, info);
3132 debug_print("file is a mail\n");
3134 debug_print("file isn't a mail\n");
3138 msglist = g_slist_reverse(msglist);
3139 folder_item_add_msgs(item, msglist, FALSE);
3140 g_slist_foreach(msglist, free_info, NULL);
3141 g_slist_free(msglist);
3142 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3144 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3146 list_free_strings_full(list);
3149 static void folderview_drag_received_cb(GtkWidget *widget,
3150 GdkDragContext *drag_context,
3153 GtkSelectionData *data,
3156 FolderView *folderview)
3159 FolderItem *item = NULL, *src_item;
3160 GtkCMCTreeNode *node;
3161 int offset = prefs_common.show_col_headers ? 24:0;
3163 folderview->scroll_value = 0;
3165 if (info == TARGET_DUMMY) {
3166 drag_state_stop(folderview);
3167 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3168 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3169 /* comes from summaryview */
3170 if (gtk_cmclist_get_selection_info
3171 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3174 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3175 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3176 src_item = folderview->summaryview->folder_item;
3178 if (item->no_select) {
3179 alertpanel_error(_("The destination folder can only be used to "
3180 "store subfolders."));
3183 /* re-check (due to acceptable possibly set for folder moves */
3184 if (!(item && item->folder && item->path && !item->no_select &&
3185 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3189 switch (gdk_drag_context_get_selected_action(drag_context)) {
3190 case GDK_ACTION_COPY:
3191 summary_copy_selected_to(folderview->summaryview, item);
3192 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3194 case GDK_ACTION_MOVE:
3195 case GDK_ACTION_DEFAULT:
3197 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3198 summary_copy_selected_to(folderview->summaryview, item);
3200 summary_move_selected_to(folderview->summaryview, item);
3201 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3204 /* comes from folderview */
3206 gboolean folder_is_normal = TRUE;
3207 gboolean copy = (GDK_ACTION_COPY ==
3208 gdk_drag_context_get_selected_action(drag_context));
3210 source = (char *)gtk_selection_data_get_data(data) + 17;
3211 if (gtk_cmclist_get_selection_info
3212 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3214 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3217 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3218 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3219 src_item = folder_find_item_from_identifier(source);
3223 src_item->stype == F_NORMAL &&
3224 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3225 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3226 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3227 !folder_has_parent_of_type(src_item, F_TRASH);
3228 if (!item || !src_item || !folder_is_normal) {
3229 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3233 folderview_move_folder(folderview, src_item, item, copy);
3234 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3236 folderview->nodes_to_recollapse = NULL;
3237 } else if (info == TARGET_MAIL_URI_LIST) {
3238 if (gtk_cmclist_get_selection_info
3239 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3242 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3244 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3245 debug_print("no node\n");
3248 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3250 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3251 debug_print("no item\n");
3254 folderview_finish_dnd(gtk_selection_data_get_data(data),
3255 drag_context, time, item);
3259 static void folderview_drag_end_cb(GtkWidget *widget,
3260 GdkDragContext *drag_context,
3261 FolderView *folderview)
3263 drag_state_stop(folderview);
3264 folderview->scroll_value = 0;
3265 g_slist_free(folderview->nodes_to_recollapse);
3266 folderview->nodes_to_recollapse = NULL;
3269 void folderview_register_popup(FolderViewPopup *fpopup)
3273 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3274 FolderView *folderview = folderviews->data;
3275 GtkActionGroup *factory;
3277 factory = create_action_group(folderview, fpopup);
3278 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3280 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3283 void folderview_unregister_popup(FolderViewPopup *fpopup)
3288 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3289 FolderView *folderview = folderviews->data;
3291 g_hash_table_remove(folderview->popups, fpopup->klass);
3293 g_hash_table_remove(folderview_popups, fpopup->klass);
3296 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3298 g_return_if_fail(folderview != NULL);
3299 g_return_if_fail(item != NULL);
3301 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3302 g_return_if_fail(ctree != NULL);
3304 GtkCMCTreeNode *node =
3305 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3306 g_return_if_fail(node != NULL);
3308 gtk_cmctree_remove_node(ctree, node);
3311 void folderview_freeze(FolderView *folderview)
3314 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3317 void folderview_thaw(FolderView *folderview)
3320 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3323 void folderview_grab_focus(FolderView *folderview)
3326 gtk_widget_grab_focus(folderview->ctree);