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_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
465 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
466 GTK_CMCTREE_EXPANDER_TRIANGLE);
468 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
469 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
471 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
472 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
474 /* don't let title buttons take key focus */
475 for (i = 0; i < N_FOLDER_COLS; i++) {
476 gtk_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
477 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
478 prefs_common.folder_col_size[i]);
479 gtk_cmclist_set_column_visibility
480 (GTK_CMCLIST(ctree), i, col_state[i].visible);
483 g_signal_connect(G_OBJECT(ctree), "key_press_event",
484 G_CALLBACK(folderview_key_pressed),
486 g_signal_connect(G_OBJECT(ctree), "button_press_event",
487 G_CALLBACK(folderview_button_pressed),
489 g_signal_connect(G_OBJECT(ctree), "popup-menu",
490 G_CALLBACK(folderview_popup_menu), folderview);
491 g_signal_connect(G_OBJECT(ctree), "button_release_event",
492 G_CALLBACK(folderview_button_released),
494 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
495 G_CALLBACK(folderview_selected), folderview);
497 /* drag-n-dropping folders on maemo is impractical as this
498 * opens the folder almost everytime */
499 g_signal_connect(G_OBJECT(ctree), "start_drag",
500 G_CALLBACK(folderview_start_drag), folderview);
502 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
503 G_CALLBACK(folderview_drag_data_get),
506 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
507 G_CALLBACK(folderview_tree_expanded),
509 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
510 G_CALLBACK(folderview_tree_collapsed),
513 g_signal_connect(G_OBJECT(ctree), "resize_column",
514 G_CALLBACK(folderview_col_resized),
518 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
519 folderview_drag_types, 2,
520 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
521 g_signal_connect(G_OBJECT(ctree), "drag_motion",
522 G_CALLBACK(folderview_drag_motion_cb),
524 g_signal_connect(G_OBJECT(ctree), "drag_leave",
525 G_CALLBACK(folderview_drag_leave_cb),
527 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
528 G_CALLBACK(folderview_drag_received_cb),
530 g_signal_connect(G_OBJECT(ctree), "drag_end",
531 G_CALLBACK(folderview_drag_end_cb),
534 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
539 void folderview_set_column_order(FolderView *folderview)
541 GtkWidget *ctree = folderview->ctree;
542 FolderItem *item = folderview_get_selected_item(folderview);
543 FolderItem *sel_item = NULL, *op_item = NULL;
544 GtkWidget *scrolledwin = folderview->scrolledwin;
546 if (folderview->drag_timer_id != 0) {
547 g_source_remove(folderview->drag_timer_id);
548 folderview->drag_timer_id = 0;
550 if (folderview->deferred_refresh_id != 0) {
551 g_source_remove(folderview->deferred_refresh_id);
552 folderview->deferred_refresh_id = 0;
554 if (folderview->scroll_timeout_id != 0) {
555 g_source_remove(folderview->scroll_timeout_id);
556 folderview->scroll_timeout_id = 0;
558 if (folderview->postpone_select_id != 0) {
559 g_source_remove(folderview->postpone_select_id);
560 folderview->postpone_select_id = 0;
563 if (folderview->selected)
564 sel_item = folderview_get_selected_item(folderview);
565 if (folderview->opened)
566 op_item = folderview_get_opened_item(folderview);
568 debug_print("recreating tree...\n");
569 gtk_widget_destroy(folderview->ctree);
572 folderview->ctree = ctree = folderview_ctree_create(folderview);
573 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
574 GTK_CMCLIST(ctree)->hadjustment);
575 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
576 GTK_CMCLIST(ctree)->vadjustment);
577 gtk_widget_show(ctree);
580 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
582 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
584 folderview_set(folderview);
585 folderview_column_set_titles(folderview);
587 folderview_select(folderview,item);
590 FolderView *folderview_create(void)
592 FolderView *folderview;
593 GtkWidget *scrolledwin;
596 debug_print("Creating folder view...\n");
597 folderview = g_new0(FolderView, 1);
599 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
600 gtk_scrolled_window_set_policy
601 (GTK_SCROLLED_WINDOW(scrolledwin),
602 GTK_POLICY_AUTOMATIC,
603 prefs_common.folderview_vscrollbar_policy);
604 gtk_widget_set_size_request(scrolledwin,
605 prefs_common.folderview_width,
606 prefs_common.folderview_height);
608 folderview->scrolledwin = scrolledwin;
609 ctree = folderview_ctree_create(folderview);
611 /* create popup factories */
612 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
613 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
615 folderview->ctree = ctree;
617 folderview->folder_update_callback_id =
618 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
619 folderview->folder_item_update_callback_id =
620 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
622 gtk_widget_show_all(scrolledwin);
624 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
625 folderview_list = g_list_append(folderview_list, folderview);
627 folderview->drag_timer_id = 0;
628 folderview->deferred_refresh_id = 0;
629 folderview->scroll_timeout_id = 0;
630 folderview->postpone_select_id = 0;
635 void folderview_init(FolderView *folderview)
637 GtkWidget *ctree = folderview->ctree;
639 PangoFontDescription *normal_font;
641 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
642 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
652 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
653 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
659 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
666 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
668 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
676 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
677 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
678 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
679 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
680 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
681 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
682 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
683 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
684 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
685 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
686 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
687 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
688 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
689 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
690 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
691 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
693 normal_font = pango_font_description_from_string(NORMAL_FONT);
695 gtk_widget_modify_font(ctree, normal_font);
696 pango_font_description_free(normal_font);
698 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
701 PangoFontDescription *font_desc;
702 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
703 font_desc = pango_font_description_from_string(NORMAL_FONT);
705 if (normal_style->font_desc)
706 pango_font_description_free
707 (normal_style->font_desc);
708 normal_style->font_desc = font_desc;
710 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
711 normal_color_style = gtk_style_copy(normal_style);
712 normal_color_style->text[GTK_STATE_NORMAL] = gdk_color;
716 gtkut_convert_int_to_gdk_color(prefs_common.color[COL_NEW], &gdk_color);
717 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
718 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
719 PangoFontDescription *font_desc;
720 font_desc = pango_font_description_from_string(NORMAL_FONT);
722 pango_font_description_free(bold_style->font_desc);
723 bold_style->font_desc = font_desc;
725 pango_font_description_set_weight
726 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
728 PangoFontDescription *font_desc;
729 font_desc = pango_font_description_from_string(BOLD_FONT);
731 if (bold_style->font_desc)
732 pango_font_description_free
733 (bold_style->font_desc);
734 bold_style->font_desc = font_desc;
737 bold_color_style = gtk_style_copy(bold_style);
738 bold_color_style->text[GTK_STATE_NORMAL] = gdk_color;
740 bold_tgtfold_style = gtk_style_copy(bold_style);
741 bold_tgtfold_style->text[GTK_STATE_NORMAL] = folderview->color_op;
745 static gboolean folderview_defer_set(gpointer data)
747 FolderView *folderview = (FolderView *)data;
748 MainWindow *mainwin = folderview->mainwin;
752 if (mainwin->lock_count)
755 debug_print("doing deferred folderview_set now\n");
756 folderview_set(folderview);
758 folderview->deferred_refresh_id = 0;
762 void folderview_set(FolderView *folderview)
764 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
765 MainWindow *mainwin = folderview->mainwin;
766 FolderItem *sel_item = NULL, *op_item = NULL;
771 if (mainwin->lock_count) {
772 if (folderview->deferred_refresh_id == 0)
773 folderview->deferred_refresh_id =
774 g_timeout_add(500, folderview_defer_set, folderview);
775 debug_print("deferred folderview_set\n");
780 debug_print("Setting folder info...\n");
781 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
783 main_window_cursor_wait(mainwin);
785 if (folderview->selected)
786 sel_item = folderview_get_selected_item(folderview);
787 if (folderview->opened)
788 op_item = folderview_get_opened_item(folderview);
790 folderview->selected = NULL;
791 folderview->opened = NULL;
793 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
794 gtk_cmclist_clear(GTK_CMCLIST(ctree));
796 folderview_set_folders(folderview);
799 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
801 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
803 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
804 main_window_cursor_normal(mainwin);
805 STATUSBAR_POP(mainwin);
809 void folderview_set_all(void)
813 for (list = folderview_list; list != NULL; list = list->next)
814 folderview_set((FolderView *)list->data);
817 void folderview_select(FolderView *folderview, FolderItem *item)
819 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
820 GtkCMCTreeNode *node;
821 GtkCMCTreeNode *old_selected = folderview->selected;
825 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
826 if (node) folderview_select_node(folderview, node);
828 if (old_selected != node)
829 folder_update_op_count();
832 static void mark_all_read_cb(GtkAction *action, gpointer data)
834 mark_all_read_unread_handler(action, data, FALSE, TRUE);
837 static void mark_all_unread_cb(GtkAction *action, gpointer data)
839 mark_all_read_unread_handler(action, data, FALSE, FALSE);
842 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
844 mark_all_read_unread_handler(action, data, TRUE, TRUE);
847 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
849 mark_all_read_unread_handler(action, data, TRUE, FALSE);
852 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
853 gboolean recursive, gboolean read)
855 FolderView *folderview = (FolderView *)data;
861 item = folderview_get_selected_item(folderview);
866 title = _("Mark all as read");
867 message = recursive? _("Do you really want to mark all mails in this "
868 "folder and its sub-folders as read?") :
869 _("Do you really want to mark all mails in this "
872 title = _("Mark all as unread");
873 message = recursive? _("Do you really want to mark all mails in this "
874 "folder and its sub-folders as unread?") :
875 _("Do you really want to mark all mails in this "
876 "folder as unread?");
878 if (prefs_common.ask_mark_all_read) {
879 val = alertpanel_full(title, message,
880 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
881 TRUE, NULL, ALERT_QUESTION);
883 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
885 else if (val & G_ALERTDISABLE)
886 prefs_common.ask_mark_all_read = FALSE;
889 folder_item_update_freeze();
890 if (folderview->summaryview->folder_item != item && !recursive)
891 summary_lock(folderview->summaryview);
893 summary_freeze(folderview->summaryview);
897 folderutils_mark_all_read_recursive(item);
899 folderutils_mark_all_read(item);
902 folderutils_mark_all_unread_recursive(item);
904 folderutils_mark_all_unread(item);
906 if (folderview->summaryview->folder_item != item && !recursive)
907 summary_unlock(folderview->summaryview);
909 summary_thaw(folderview->summaryview);
910 folder_item_update_thaw();
913 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
915 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
917 cm_return_if_fail(node != NULL);
919 if (folderview->open_folder) {
923 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
924 gtkut_ctree_expand_parent_all(ctree, node);
926 folderview->open_folder = TRUE;
927 gtkut_ctree_set_focus_row(ctree, node);
928 gtk_cmctree_select(ctree, node);
929 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
930 if ((folderview->summaryview->folder_item &&
931 folderview->summaryview->folder_item->total_msgs > 0) ||
932 prefs_common.layout_mode == SMALL_LAYOUT)
933 summary_select_node(folderview->summaryview,
934 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
936 gtk_widget_grab_focus(folderview->ctree);
939 void folderview_unselect(FolderView *folderview)
941 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
943 (GTK_CMCTREE(folderview->ctree), folderview->opened);
945 folderview->selected = folderview->opened = NULL;
948 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
949 GtkCMCTreeNode *node,
955 node = gtkut_ctree_node_next(ctree, node);
957 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
959 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
960 item = gtk_cmctree_node_get_row_data(ctree, node);
963 if (item->stype == F_TRASH || item->stype == F_DRAFT)
967 if(item->unread_msgs > 0)
971 if(item->new_msgs > 0)
975 if(item->marked_msgs > 0)
979 if(item->total_msgs > 0)
988 void folderview_select_next_with_flag(FolderView *folderview,
991 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
992 GtkCMCTreeNode *node = NULL;
993 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
997 prefs_common.summary_select_prio[0] = ACTION_OLDEST_UNREAD;
1000 prefs_common.summary_select_prio[0] = ACTION_OLDEST_NEW;
1003 prefs_common.summary_select_prio[0] = ACTION_OLDEST_MARKED;
1006 prefs_common.summary_select_prio[0] = ACTION_OLDEST_LIST;
1010 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1012 folderview_select_node(folderview, node);
1016 if (!folderview->opened ||
1017 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1021 /* search again from the first node */
1022 node = folderview_find_next_with_flag(ctree, NULL, flag);
1024 folderview_select_node(folderview, node);
1027 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1030 FolderItem *folderview_get_selected_item(FolderView *folderview)
1032 g_return_val_if_fail(folderview != NULL, NULL);
1033 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1035 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1037 if (!folderview->selected) return NULL;
1038 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1041 FolderItem *folderview_get_opened_item(FolderView *folderview)
1043 g_return_val_if_fail(folderview != NULL, NULL);
1044 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1046 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1048 if (!folderview->opened) return NULL;
1049 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1052 static void folderview_set_folders(FolderView *folderview)
1055 list = folder_get_list();
1057 for (; list != NULL; list = list->next) {
1058 folderview_append_folder(folderview, FOLDER(list->data));
1062 static gchar *get_scan_str(FolderItem *item)
1065 return g_strdup_printf(_("Scanning folder %s/%s..."),
1066 item->folder->name, item->path);
1068 return g_strdup_printf(_("Scanning folder %s..."),
1069 item->folder->name);
1071 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1075 for (list = folderview_list; list != NULL; list = list->next) {
1076 FolderView *folderview = (FolderView *)list->data;
1077 MainWindow *mainwin = folderview->mainwin;
1078 gchar *str = get_scan_str(item);
1080 STATUSBAR_PUSH(mainwin, str);
1081 STATUSBAR_POP(mainwin);
1086 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1089 MainWindow *mainwin = mainwindow_get_mainwindow();
1090 FolderView *folderview = NULL;
1091 GtkAdjustment *pos = NULL;
1094 cm_return_if_fail(folder != NULL);
1096 if (!folder->klass->scan_tree) return;
1099 alertpanel_full(_("Rebuild folder tree"),
1100 _("Rebuilding the folder tree will remove "
1101 "local caches. Do you want to continue?"),
1102 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
1103 FALSE, NULL, ALERT_WARNING)
1104 != G_ALERTALTERNATE) {
1110 window = label_window_create(_("Rebuilding folder tree..."));
1112 window = label_window_create(_("Scanning folder tree..."));
1115 folderview = mainwin->folderview;
1118 pos = gtk_scrolled_window_get_vadjustment(
1119 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1120 height = gtk_adjustment_get_value(pos);
1123 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1124 folder_scan_tree(folder, rebuild);
1125 folder_set_ui_func(folder, NULL, NULL);
1127 folderview_set_all();
1130 pos = gtk_scrolled_window_get_vadjustment(
1131 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1132 gtk_adjustment_set_value(pos, height);
1133 gtk_adjustment_changed(pos);
1135 label_window_destroy(window);
1139 /** folderview_check_new()
1140 * Scan and update the folder and return the
1141 * count the number of new messages since last check.
1142 * \param folder the folder to check for new messages
1143 * \return the number of new messages since last check
1145 gint folderview_check_new(Folder *folder)
1149 FolderView *folderview;
1151 GtkCMCTreeNode *node;
1153 gint former_new_msgs = 0;
1154 gint former_new = 0, former_unread = 0, former_total;
1156 for (list = folderview_list; list != NULL; list = list->next) {
1157 folderview = (FolderView *)list->data;
1158 ctree = GTK_CMCTREE(folderview->ctree);
1159 folderview->scanning_folder = folder;
1161 main_window_lock(folderview->mainwin);
1163 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1164 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1166 item = gtk_cmctree_node_get_row_data(ctree, node);
1167 if (!item || !item->path || !item->folder) continue;
1168 if (item->no_select) continue;
1169 if (folder && folder != item->folder) continue;
1170 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1171 if (!item->prefs->newmailcheck) continue;
1172 if (item->processing_pending == TRUE) {
1173 debug_print("skipping %s, processing pending\n",
1174 item->path ? item->path : item->name);
1177 if (item->scanning != ITEM_NOT_SCANNING) {
1178 debug_print("skipping %s, scanning\n",
1179 item->path ? item->path : item->name);
1183 str = get_scan_str(item);
1185 STATUSBAR_PUSH(folderview->mainwin, str);
1189 folderview_scan_tree_func(item->folder, item, NULL);
1190 former_new = item->new_msgs;
1191 former_unread = item->unread_msgs;
1192 former_total = item->total_msgs;
1194 if (item->folder->klass->scan_required &&
1195 (item->folder->klass->scan_required(item->folder, item) ||
1196 item->folder->inbox == item ||
1197 item->opened == TRUE ||
1198 item->processing_pending == TRUE)) {
1199 if (folder_item_scan(item) < 0) {
1201 summaryview_unlock(folderview->summaryview, item);
1202 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1203 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1204 item->path ? item->path:item->name);
1205 STATUSBAR_POP(folderview->mainwin);
1207 } else if (!FOLDER_IS_LOCAL(folder)) {
1208 STATUSBAR_POP(folderview->mainwin);
1213 } else if (!item->folder->klass->scan_required) {
1214 if (folder_item_scan(item) < 0) {
1215 summaryview_unlock(folderview->summaryview, item);
1216 if (folder && !FOLDER_IS_LOCAL(folder)) {
1217 STATUSBAR_POP(folderview->mainwin);
1222 if (former_new != item->new_msgs ||
1223 former_unread != item->unread_msgs ||
1224 former_total != item->total_msgs)
1225 folderview_update_node(folderview, node);
1227 new_msgs += item->new_msgs;
1228 former_new_msgs += former_new;
1229 STATUSBAR_POP(folderview->mainwin);
1231 folderview->scanning_folder = NULL;
1232 main_window_unlock(folderview->mainwin);
1236 folder_write_list();
1237 /* Number of new messages since last check is the just the difference
1238 * between former_new_msgs and new_msgs. If new_msgs is less than
1239 * former_new_msgs, that would mean another session accessed the folder
1240 * and the result is not well defined.
1242 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1246 void folderview_check_new_all(void)
1250 FolderView *folderview;
1252 folderview = (FolderView *)folderview_list->data;
1255 main_window_lock(folderview->mainwin);
1256 window = label_window_create
1257 (_("Checking for new messages in all folders..."));
1259 list = folder_get_list();
1260 for (; list != NULL; list = list->next) {
1261 Folder *folder = list->data;
1263 folderview_check_new(folder);
1266 folder_write_list();
1267 folderview_set_all();
1269 label_window_destroy(window);
1270 main_window_unlock(folderview->mainwin);
1274 static gboolean folderview_have_children_sub(FolderView *folderview,
1280 if (!item || !item->folder || !item->folder->node)
1283 node = item->folder->node;
1285 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1286 node = node->children;
1288 if (in_sub && item->total_msgs > 0) {
1292 while (node != NULL) {
1293 if (node && node->data) {
1294 FolderItem *next_item = (FolderItem*) node->data;
1296 if (folderview_have_children_sub(folderview,
1305 static gboolean folderview_have_children(FolderView *folderview,
1308 return folderview_have_children_sub(folderview, item, FALSE);
1311 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1317 if (!item || !item->folder || !item->folder->node)
1320 node = item->folder->node;
1322 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1323 node = node->children;
1326 (item->new_msgs > 0 ||
1327 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1331 while (node != NULL) {
1332 if (node && node->data) {
1333 FolderItem *next_item = (FolderItem*) node->data;
1335 if (folderview_have_new_children_sub(folderview,
1344 static gboolean folderview_have_new_children(FolderView *folderview,
1347 return folderview_have_new_children_sub(folderview, item, FALSE);
1350 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1356 if (!item || !item->folder || !item->folder->node)
1359 node = item->folder->node;
1361 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1362 node = node->children;
1365 (item->unread_msgs > 0 ||
1366 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1370 while (node != NULL) {
1371 if (node && node->data) {
1372 FolderItem *next_item = (FolderItem*) node->data;
1374 if (folderview_have_unread_children_sub(folderview,
1384 static gboolean folderview_have_unread_children(FolderView *folderview,
1387 return folderview_have_unread_children_sub(folderview, item, FALSE);
1390 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1396 if (!item || !item->folder || !item->folder->node) {
1400 node = item->folder->node;
1402 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1403 node = node->children;
1406 (((item->total_msgs > 0) &&
1407 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1411 while (node != NULL) {
1412 if (node && node->data) {
1413 FolderItem *next_item = (FolderItem*) node->data;
1415 if (folderview_have_read_children_sub(folderview,
1426 static gboolean folderview_have_read_children(FolderView *folderview,
1429 return folderview_have_read_children_sub(folderview, item, FALSE);
1432 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1438 if (!item || !item->folder || !item->folder->node)
1441 node = item->folder->node;
1443 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1444 node = node->children;
1446 if (in_sub && item->search_match){
1450 while (node != NULL) {
1451 if (node && node->data) {
1452 FolderItem *next_item = (FolderItem*) node->data;
1454 if (folderview_have_matching_children_sub(folderview,
1464 static gboolean folderview_have_matching_children(FolderView *folderview,
1467 return folderview_have_matching_children_sub(folderview, item, FALSE);
1470 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1476 if (!item || !item->folder || !item->folder->node)
1479 node = item->folder->node;
1481 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1482 node = node->children;
1484 if (item->marked_msgs != 0) {
1488 while (node != NULL) {
1489 if (node && node->data) {
1490 FolderItem *next_item = (FolderItem*) node->data;
1492 if (folderview_have_marked_children_sub(folderview,
1501 static gboolean folderview_have_marked_children(FolderView *folderview,
1504 return folderview_have_marked_children_sub(folderview, item, FALSE);
1507 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1509 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1510 GtkStyle *style = NULL;
1511 GtkStyle *color_style = NULL;
1513 GdkPixbuf *xpm, *openxpm;
1514 static GdkPixbuf *searchicon;
1515 gboolean mark = FALSE;
1518 gboolean add_unread_mark;
1519 gboolean add_sub_match_mark;
1520 gboolean use_bold, use_color;
1521 gint *col_pos = folderview->col_pos;
1522 SpecialFolderItemType stype;
1524 item = gtk_cmctree_node_get_row_data(ctree, node);
1525 cm_return_if_fail(item != NULL);
1527 if (!GTK_CMCTREE_ROW(node)->expanded)
1528 mark = folderview_have_marked_children(folderview, item);
1530 mark = (item->marked_msgs != 0);
1532 stype = item->stype;
1533 if (stype == F_NORMAL) {
1534 if (folder_has_parent_of_type(item, F_TRASH))
1536 else if (folder_has_parent_of_type(item, F_DRAFT))
1538 else if (folder_has_parent_of_type(item, F_OUTBOX))
1540 else if (folder_has_parent_of_type(item, F_QUEUE))
1545 if (item->hide_read_msgs || item->hide_read_threads) {
1546 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1547 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1549 xpm = mark?m_inboxxpm:inboxxpm;
1550 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1554 if (item->hide_read_msgs || item->hide_read_threads) {
1555 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1556 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1558 xpm = mark?m_outboxxpm:outboxxpm;
1559 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1563 if (item->hide_read_msgs || item->hide_read_threads) {
1564 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1565 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1567 xpm = mark?m_queuexpm:queuexpm;
1568 openxpm = mark?m_queueopenxpm:queueopenxpm;
1572 if (item->hide_read_msgs || item->hide_read_threads) {
1573 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1574 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1576 xpm = mark?m_trashxpm:trashxpm;
1577 openxpm = mark?m_trashopenxpm:trashopenxpm;
1581 xpm = mark?m_draftsxpm:draftsxpm;
1582 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1586 FOLDER_TYPE(item->folder) == F_IMAP &&
1587 item->folder->account->imap_subsonly) {
1588 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1589 openxpm = foldersubsopenxpm;
1590 } else if (item->no_select) {
1591 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1592 openxpm = foldernoselectopenxpm;
1593 } else if (item->hide_read_msgs || item->hide_read_threads) {
1594 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1595 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1597 xpm = mark?m_folderxpm:folderxpm;
1598 openxpm = mark?m_folderopenxpm:folderopenxpm;
1602 name = folder_item_get_name(item);
1604 if (!GTK_CMCTREE_ROW(node)->expanded) {
1605 add_unread_mark = folderview_have_unread_children(
1607 add_sub_match_mark = folderview_have_matching_children(
1610 add_unread_mark = FALSE;
1611 add_sub_match_mark = FALSE;
1614 if (item->search_match) {
1616 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1619 xpm = openxpm = searchicon;
1623 if (prefs_common.display_folder_unread) {
1624 if (folder_has_parent_of_type(item, F_QUEUE)) {
1625 /* only total_msgs matters here */
1626 if (item->total_msgs > 0) {
1627 /* show total number (should be equal to the unread number)
1629 str = g_strdup_printf("%s (%d%s%s)",
1630 name, item->total_msgs,
1631 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1632 (item->unreadmarked_msgs > 0) ? "!" : "");
1635 if (prefs_common.display_folder_unread == 1) {
1636 if (item->unread_msgs > 0) {
1637 /* show unread number and signs */
1638 str = g_strdup_printf("%s (%d%s%s)",
1639 name, item->unread_msgs,
1640 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1641 (item->unreadmarked_msgs > 0) ? "!" : "");
1644 if (item->total_msgs > 0) {
1645 /* show unread number, total number and signs if any */
1646 str = g_strdup_printf("%s (%d/%d%s%s)",
1647 name, item->unread_msgs, item->total_msgs,
1648 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1649 (item->unreadmarked_msgs > 0) ? "!" : "");
1653 if ((str == NULL) &&
1654 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1655 /* no unread/total numbers, but at least one sign */
1656 str = g_strdup_printf("%s (%s%s)",
1658 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1659 (item->unreadmarked_msgs > 0) ? "!" : "");
1663 /* last fallback, folder name only or with +! sign */
1664 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1665 str = g_strdup_printf("%s%s",
1667 } else if (item->unreadmarked_msgs > 0) {
1668 str = g_strdup_printf("%s%s",
1670 } else if (add_sub_match_mark) {
1671 str = g_strdup_printf("%s%s",
1674 str = g_strdup_printf("%s", name);
1677 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1679 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1683 if (!folder_item_parent(item)) {
1684 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1685 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1689 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1693 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1694 folder_has_parent_of_type(item, F_TRASH)) {
1695 use_bold = use_color = FALSE;
1696 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1697 GSList *list = folder_item_get_msg_list(item);
1699 use_bold = use_color = FALSE;
1700 for (cur = list; cur; cur = cur->next) {
1701 MsgInfo *msginfo = (MsgInfo *)cur->data;
1702 if (!MSG_IS_DELETED(msginfo->flags)) {
1703 /* highlight queue folder if there are any messages */
1704 use_bold = use_color = TRUE;
1708 if (!GTK_CMCTREE_ROW(node)->expanded &&
1709 use_bold == FALSE &&
1710 folderview_have_children(folderview, item))
1711 use_bold = use_color = TRUE;
1712 procmsg_msg_list_free(list);
1714 /* if unread messages exist, print with bold font */
1715 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1717 /* if new messages exist, print with colored letter */
1719 (item->new_msgs > 0) ||
1721 folderview_have_new_children(folderview, item));
1724 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1729 if (item->prefs->color > 0 && !use_color) {
1730 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1731 color_style = gtk_style_copy(bold_style);
1732 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1733 style = color_style;
1734 } else if (use_color) {
1735 style = bold_color_style;
1738 if (item->op_count > 0) {
1739 style = bold_tgtfold_style;
1741 } else if (use_color) {
1742 style = normal_color_style;
1743 gtk_cmctree_node_set_foreground(ctree, node,
1744 &folderview->color_new);
1745 } else if (item->op_count > 0) {
1746 style = bold_tgtfold_style;
1747 } else if (item->prefs->color > 0) {
1749 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1750 color_style = gtk_style_copy(normal_style);
1751 color_style->text[GTK_STATE_NORMAL] = gdk_color;
1752 style = color_style;
1754 style = normal_style;
1757 gtk_cmctree_node_set_row_style(ctree, node, style);
1759 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1760 folderview_update_node(folderview, node);
1763 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1766 FolderView *folderview;
1768 GtkCMCTreeNode *node;
1770 cm_return_if_fail(item != NULL);
1772 for (list = folderview_list; list != NULL; list = list->next) {
1773 folderview = (FolderView *)list->data;
1774 ctree = GTK_CMCTREE(folderview->ctree);
1776 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1777 if (node && item->search_match != matches) {
1778 item->search_match = matches;
1779 folderview_update_node(folderview, node);
1784 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1786 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1787 FolderView *folderview = (FolderView *)data;
1789 GtkCMCTreeNode *node;
1790 cm_return_val_if_fail(update_info != NULL, TRUE);
1791 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1792 cm_return_val_if_fail(folderview != NULL, FALSE);
1794 ctree = GTK_CMCTREE(folderview->ctree);
1796 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1799 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1800 folderview_update_node(folderview, node);
1802 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1803 update_info->item == folderview->summaryview->folder_item &&
1804 update_info->item != NULL)
1805 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1806 summary_show(folderview->summaryview, update_info->item);
1812 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1813 GNode *gnode, GtkCMCTreeNode *cnode,
1816 FolderView *folderview = (FolderView *)data;
1817 FolderItem *item = FOLDER_ITEM(gnode->data);
1819 cm_return_val_if_fail(item != NULL, FALSE);
1821 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1822 folderview_update_node(folderview, cnode);
1827 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1830 FolderView *folderview = (FolderView *)data;
1833 if (GTK_CMCTREE_ROW(node)->children) {
1834 item = gtk_cmctree_node_get_row_data(ctree, node);
1835 cm_return_if_fail(item != NULL);
1837 if (!item->collapsed)
1838 gtk_cmctree_expand(ctree, node);
1840 folderview_update_node(folderview, node);
1844 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1845 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1848 GtkCMCTreeNode *node, *parent, *sibling;
1850 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1852 g_warning("%s not found.", item->path);
1854 parent = GTK_CMCTREE_ROW(node)->parent;
1855 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1856 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1858 sibling = GTK_CMCTREE_ROW(parent)->children;
1862 tmp = gtk_cmctree_node_get_row_data
1864 if (tmp && tmp->stype != F_NORMAL)
1865 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1869 if (node != sibling)
1870 gtk_cmctree_move(ctree, node, parent, sibling);
1877 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1880 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1881 GtkCMCTreeNode *prev = NULL;
1883 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1884 gtk_sctree_sort_recursive(ctree, root);
1885 if (root && GTK_CMCTREE_ROW(root)->parent) {
1886 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1889 set_special_folder(ctree, folder->inbox, root, &prev);
1890 set_special_folder(ctree, folder->outbox, root, &prev);
1891 set_special_folder(ctree, folder->draft, root, &prev);
1892 set_special_folder(ctree, folder->queue, root, &prev);
1893 set_special_folder(ctree, folder->trash, root, &prev);
1894 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1897 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1899 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1900 GtkCMCTreeNode *root;
1902 cm_return_if_fail(folder != NULL);
1904 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1905 folderview_gnode_func, folderview);
1906 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1908 folderview_sort_folders(folderview, root, folder);
1911 /* callback functions */
1912 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1913 GdkEventButton *event)
1917 FolderViewPopup *fpopup;
1918 GtkActionGroup *action_group;
1920 FolderItem *special_trash = NULL, *special_queue = NULL;
1922 GtkUIManager *ui_manager = gtk_ui_manager_new();
1924 if (folderview->ui_manager)
1925 g_object_unref(folderview->ui_manager);
1927 folderview->ui_manager = ui_manager;
1928 item = folderview_get_selected_item(folderview);
1930 cm_return_if_fail(item != NULL);
1931 cm_return_if_fail(item->folder != NULL);
1932 folder = item->folder;
1934 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1937 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1939 fpopup = g_hash_table_lookup(folderview_popups, "common");
1940 action_group = g_hash_table_lookup(folderview->popups, "common");
1943 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1944 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1945 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1947 if (fpopup->add_menuitems)
1948 fpopup->add_menuitems(ui_manager, item);
1950 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1960 if (fpopup->set_sensitivity != NULL)
1961 fpopup->set_sensitivity(ui_manager, item);
1963 if (NULL != (ac = account_find_from_item(item))) {
1964 special_trash = account_get_special_folder(ac, F_TRASH);
1965 special_queue = account_get_special_folder(ac, F_QUEUE);
1968 if ((item == folder->trash || item == special_trash
1969 || folder_has_parent_of_type(item, F_TRASH))) {
1970 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1971 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1974 if ((item == folder->queue || item == special_queue
1975 || folder_has_parent_of_type(item, F_QUEUE))) {
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1980 #define SET_SENS(name, sens) \
1981 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1983 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1984 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1985 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1986 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1987 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1988 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1989 folderview->selected == folderview->opened);
1990 SET_SENS("FolderViewPopup/Properties", TRUE);
1992 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1993 item->total_msgs >= 1 && !item->processing_pending);
1994 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1995 !item->no_select && !item->processing_pending);
1997 if (item == folder->trash || item == special_trash
1998 || folder_has_parent_of_type(item, F_TRASH)) {
1999 GSList *msglist = folder_item_get_msg_list(item);
2000 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2001 procmsg_msg_list_free(msglist);
2003 if (item == folder->queue || item == special_queue
2004 || folder_has_parent_of_type(item, F_QUEUE)) {
2005 GSList *msglist = folder_item_get_msg_list(item);
2006 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2007 procmsg_msg_list_free(msglist);
2011 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2012 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2013 g_signal_connect(G_OBJECT(popup), "selection_done",
2014 G_CALLBACK(folderview_popup_close),
2016 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2017 event->button, event->time);
2020 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2021 FolderView *folderview)
2023 GtkCMCList *clist = GTK_CMCLIST(ctree);
2024 gint prev_row = -1, row = -1, column = -1;
2026 if (!event) 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 (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2156 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2157 folderview->selected);
2159 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2160 folderview->selected))) {
2161 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2162 NULL, folder_item_parent(item)))) {
2163 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2164 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2165 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2174 if (event->keyval == GDK_KEY_Home)
2175 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2177 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2178 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2180 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2182 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2183 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2193 typedef struct _PostponedSelectData
2196 GtkCMCTreeNode *row;
2198 FolderView *folderview;
2199 } PostponedSelectData;
2201 static gboolean postpone_select(void *data)
2203 PostponedSelectData *psdata = (PostponedSelectData *)data;
2204 debug_print("trying again\n");
2206 psdata->folderview->postpone_select_id = 0;
2207 psdata->folderview->open_folder = TRUE;
2208 main_window_cursor_normal(psdata->folderview->mainwin);
2209 STATUSBAR_POP(psdata->folderview->mainwin);
2210 folderview_selected(psdata->ctree, psdata->row,
2211 psdata->column, psdata->folderview);
2216 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2218 if (folderview->opened) {
2220 folderview->opened = NULL;
2224 FolderItem *olditem =
2225 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2226 folderview->opened);
2228 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2229 olditem->path ? olditem->path:olditem->name);
2230 /* will be null if we just moved the previously opened folder */
2231 STATUSBAR_PUSH(folderview->mainwin, buf);
2232 main_window_cursor_wait(folderview->mainwin);
2234 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2235 summary_show(folderview->summaryview, NULL);
2236 folder_item_close(olditem);
2237 main_window_cursor_normal(folderview->mainwin);
2238 STATUSBAR_POP(folderview->mainwin);
2239 if (olditem->folder->klass->item_closed)
2240 olditem->folder->klass->item_closed(olditem);
2245 if (folderview->opened &&
2246 !GTK_CMCTREE_ROW(folderview->opened)->children)
2247 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2249 folderview->opened = NULL;
2251 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2252 gint column, FolderView *folderview)
2254 static gboolean can_select = TRUE; /* exclusive lock */
2259 GtkCMCTreeNode *old_opened = folderview->opened;
2261 folderview->selected = row;
2263 debug_print("newly selected %p, opened %p\n", folderview->selected,
2264 folderview->opened);
2265 if (folderview->opened == row) {
2266 folderview->open_folder = FALSE;
2271 item = gtk_cmctree_node_get_row_data(ctree, row);
2274 folderview->open_folder = FALSE;
2278 if (!can_select || summary_is_locked(folderview->summaryview)) {
2279 if (folderview->opened) {
2280 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2281 gtk_cmctree_select(ctree, folderview->opened);
2283 folderview->open_folder = FALSE;
2288 if (!folderview->open_folder) {
2295 /* Save cache for old folder */
2296 /* We don't want to lose all caches if sylpheed crashed */
2297 /* resets folderview->opened to NULL */
2298 folderview_close_opened(folderview, FALSE);
2300 /* CLAWS: set compose button type: news folder items
2301 * always have a news folder as parent */
2303 toolbar_set_compose_button
2304 (folderview->mainwin->toolbar,
2305 FOLDER_TYPE(item->folder) == F_NEWS ?
2306 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2309 debug_print("Folder %s is selected\n", item->path);
2311 if (!GTK_CMCTREE_ROW(row)->children)
2312 gtk_cmctree_expand(ctree, row);
2314 /* ungrab the mouse event */
2315 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2316 gtk_grab_remove(GTK_WIDGET(ctree));
2317 if (gdk_pointer_is_grabbed())
2318 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2322 /* TODO: wwp: avoid displaying (null) in the status bar */
2323 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2324 item->path : "(null)");
2325 debug_print("%s\n", buf);
2326 STATUSBAR_PUSH(folderview->mainwin, buf);
2329 main_window_cursor_wait(folderview->mainwin);
2331 if (folderview->scanning_folder == item->folder) {
2334 res = folder_item_open(item);
2337 if (res == -1 && item->no_select == FALSE) {
2338 main_window_cursor_normal(folderview->mainwin);
2339 STATUSBAR_POP(folderview->mainwin);
2341 alertpanel_error(_("Folder could not be opened."));
2343 folderview->open_folder = FALSE;
2347 } else if (res == -2 && item->no_select == FALSE) {
2348 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2349 data->ctree = ctree;
2351 data->column = column;
2352 data->folderview = folderview;
2353 debug_print("postponing open of %s till end of scan\n",
2354 item->path ? item->path:item->name);
2355 folderview->open_folder = FALSE;
2357 if (folderview->postpone_select_id != 0)
2358 g_source_remove(folderview->postpone_select_id);
2359 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2364 main_window_cursor_normal(folderview->mainwin);
2367 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2368 opened = summary_show(folderview->summaryview, item);
2370 folder_clean_cache_memory(item);
2373 gtkut_ctree_set_focus_row(ctree, old_opened);
2374 gtk_cmctree_select(ctree, old_opened);
2375 folderview->opened = old_opened;
2377 folderview->opened = row;
2378 if (gtk_cmctree_node_is_visible(ctree, row)
2379 != GTK_VISIBILITY_FULL)
2380 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2383 STATUSBAR_POP(folderview->mainwin);
2385 folderview->open_folder = FALSE;
2390 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2391 FolderView *folderview)
2395 item = gtk_cmctree_node_get_row_data(ctree, node);
2396 cm_return_if_fail(item != NULL);
2397 item->collapsed = FALSE;
2398 folderview_update_node(folderview, node);
2401 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2402 FolderView *folderview)
2406 item = gtk_cmctree_node_get_row_data(ctree, node);
2407 cm_return_if_fail(item != NULL);
2408 item->collapsed = TRUE;
2409 folderview_update_node(folderview, node);
2412 static void folderview_popup_close(GtkMenuShell *menu_shell,
2413 FolderView *folderview)
2415 if (!folderview->opened) return;
2417 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2420 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2421 FolderView *folderview)
2423 FolderColumnType type = folderview->col_state[column].type;
2425 prefs_common.folder_col_size[type] = width;
2428 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2430 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2431 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2432 GtkCMCTreeNode *node, *parent_node;
2433 gint *col_pos = folderview->col_pos;
2434 FolderItemUpdateData hookdata;
2436 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2437 if (parent_node == NULL)
2440 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2442 text[col_pos[F_COL_FOLDER]] = item->name;
2443 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2448 gtk_cmctree_expand(ctree, parent_node);
2449 gtk_cmctree_node_set_row_data(ctree, node, item);
2451 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2452 folderview_sort_folders(folderview, parent_node, item->folder);
2454 hookdata.item = item;
2455 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2456 hookdata.msg = NULL;
2457 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2459 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2462 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2464 FolderView *folderview = (FolderView *)data;
2466 GSList *mlist = NULL;
2468 FolderItem *special_trash = NULL;
2471 if (!folderview->selected) return;
2472 item = folderview_get_selected_item(folderview);
2473 cm_return_if_fail(item != NULL);
2474 cm_return_if_fail(item->folder != NULL);
2476 if (NULL != (ac = account_find_from_item(item)))
2477 special_trash = account_get_special_folder(ac, F_TRASH);
2479 if (item != item->folder->trash && item != special_trash
2480 && !folder_has_parent_of_type(item, F_TRASH)) return;
2482 if (prefs_common.ask_on_clean) {
2483 if (alertpanel(_("Empty trash"),
2484 _("Delete all messages in trash?"),
2485 GTK_STOCK_CANCEL, _("_Empty trash"), NULL,
2486 ALERTFOCUS_SECOND) != G_ALERTALTERNATE)
2490 mlist = folder_item_get_msg_list(item);
2492 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2493 MsgInfo * msginfo = (MsgInfo *) cur->data;
2494 if (MSG_IS_LOCKED(msginfo->flags))
2496 /* is it partially received? (partial_recv isn't cached) */
2497 if (msginfo->total_size != 0 &&
2498 msginfo->size != (off_t)msginfo->total_size)
2499 partial_mark_for_delete(msginfo);
2501 procmsg_msg_list_free(mlist);
2503 folder_item_remove_all_msg(item);
2506 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2508 FolderView *folderview = (FolderView *)data;
2510 FolderItem *special_queue = NULL;
2512 gchar *errstr = NULL;
2514 if (!folderview->selected) return;
2515 item = folderview_get_selected_item(folderview);
2516 cm_return_if_fail(item != NULL);
2517 cm_return_if_fail(item->folder != NULL);
2519 if (NULL != (ac = account_find_from_item(item)))
2520 special_queue = account_get_special_folder(ac, F_QUEUE);
2522 if (item != item->folder->queue && item != special_queue
2523 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2525 if (procmsg_queue_is_empty(item))
2528 if (prefs_common.work_offline)
2529 if (alertpanel(_("Offline warning"),
2530 _("You're working offline. Override?"),
2531 GTK_STOCK_NO, GTK_STOCK_YES,
2532 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2535 /* ask for confirmation before sending queued messages only
2536 in online mode and if there is at least one message queued
2537 in any of the folder queue
2539 if (prefs_common.confirm_send_queued_messages) {
2540 if (!prefs_common.work_offline) {
2541 if (alertpanel(_("Send queued messages"),
2542 _("Send all queued messages?"),
2543 GTK_STOCK_CANCEL, _("_Send"),
2544 NULL, ALERTFOCUS_FIRST) != G_ALERTALTERNATE)
2549 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2551 alertpanel_error_log(_("Some errors occurred while "
2552 "sending queued messages."));
2554 alertpanel_error_log(_("Some errors occurred "
2555 "while sending queued messages:\n%s"), errstr);
2561 static void folderview_search_cb(GtkAction *action, gpointer data)
2563 FolderView *folderview = (FolderView *)data;
2564 summary_search(folderview->summaryview);
2567 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2569 FolderView *folderview = (FolderView *)data;
2572 if (!folderview->selected) return;
2574 item = folderview_get_selected_item(folderview);
2575 cm_return_if_fail(item != NULL);
2576 cm_return_if_fail(item->folder != NULL);
2578 item->processing_pending = TRUE;
2579 folder_item_apply_processing(item);
2580 item->processing_pending = FALSE;
2583 static void folderview_property_cb(GtkAction *action, gpointer data)
2585 FolderView *folderview = (FolderView *)data;
2588 if (!folderview->selected) return;
2590 item = folderview_get_selected_item(folderview);
2591 cm_return_if_fail(item != NULL);
2592 cm_return_if_fail(item->folder != NULL);
2594 prefs_folder_item_open(item);
2597 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2599 GSList *list = NULL;
2600 GSList *done = NULL;
2601 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2603 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2604 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2605 && list->data != node) {
2606 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2607 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2610 for (list = done; list != NULL; list = g_slist_next(list)) {
2611 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2617 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2618 FolderItem *to_folder, gboolean copy)
2620 FolderItem *new_folder = NULL;
2624 cm_return_if_fail(folderview != NULL);
2625 cm_return_if_fail(from_folder != NULL);
2626 cm_return_if_fail(to_folder != NULL);
2628 if (prefs_common.warn_dnd) {
2629 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2630 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2631 from_folder->name, to_folder->name);
2632 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2633 GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST,
2634 TRUE, NULL, ALERT_QUESTION);
2637 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2639 else if (status & G_ALERTDISABLE)
2640 prefs_common.warn_dnd = FALSE;
2643 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2644 from_folder->name, to_folder->name);
2645 STATUSBAR_PUSH(folderview->mainwin, buf);
2647 summary_clear_all(folderview->summaryview);
2648 folderview->opened = NULL;
2649 folderview->selected = NULL;
2650 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2652 main_window_cursor_wait(folderview->mainwin);
2654 statusbar_verbosity_set(FALSE);
2655 folder_item_update_freeze();
2656 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2657 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2658 statusbar_verbosity_set(FALSE);
2659 main_window_cursor_normal(folderview->mainwin);
2660 STATUSBAR_POP(folderview->mainwin);
2661 folder_item_update_thaw();
2662 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2664 folderview_sort_folders(folderview,
2665 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2666 NULL, to_folder), new_folder->folder);
2667 folderview_select(folderview, new_folder);
2668 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2670 statusbar_verbosity_set(FALSE);
2671 main_window_cursor_normal(folderview->mainwin);
2672 STATUSBAR_POP(folderview->mainwin);
2673 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2674 folder_item_update_thaw();
2676 case F_MOVE_FAILED_DEST_IS_PARENT:
2677 alertpanel_error(_("Source and destination are the same."));
2679 case F_MOVE_FAILED_DEST_IS_CHILD:
2680 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2681 _("Can't move a folder to one of its children."));
2683 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2684 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2687 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2692 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2695 static gint folderview_clist_compare(GtkCMCList *clist,
2696 gconstpointer ptr1, gconstpointer ptr2)
2698 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2699 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2701 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2703 return item1->order - item2->order;
2706 // if only one folder has an order it comes first
2707 if (item1->order > 0)
2711 if (item2->order > 0)
2717 return (item2->name != NULL);
2721 return g_utf8_collate(item1->name, item2->name);
2724 static void folderview_processing_cb(GtkAction *action, gpointer data)
2726 FolderView *folderview = (FolderView *)data;
2730 if (!folderview->selected) return;
2732 item = folderview_get_selected_item(folderview);
2733 cm_return_if_fail(item != NULL);
2734 cm_return_if_fail(item->folder != NULL);
2736 id = folder_item_get_identifier(item);
2737 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2740 prefs_filtering_open(&item->prefs->processing, title,
2741 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2745 void folderview_set_target_folder_color(gint color_op)
2749 FolderView *folderview;
2751 for (list = folderview_list; list != NULL; list = list->next) {
2752 folderview = (FolderView *)list->data;
2753 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2755 bold_tgtfold_style->text[GTK_STATE_NORMAL] =
2756 folderview->color_op;
2762 static gchar *last_smallfont = NULL;
2763 static gchar *last_normalfont = NULL;
2764 static gchar *last_boldfont = NULL;
2765 static gboolean last_derive = 0;
2767 void folderview_reinit_fonts(FolderView *folderview)
2770 g_free(last_smallfont);
2771 last_smallfont = NULL;
2772 g_free(last_normalfont);
2773 last_normalfont = NULL;
2774 g_free(last_boldfont);
2775 last_boldfont = NULL;
2778 void folderview_reflect_prefs(void)
2780 gboolean update_font = FALSE;
2781 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2782 FolderItem *item = folderview_get_selected_item(folderview);
2783 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2784 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2785 gint height = gtk_adjustment_get_value(pos);
2787 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2788 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2789 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2790 last_derive != prefs_common.derive_from_normal_font)
2796 g_free(last_smallfont);
2797 last_smallfont = g_strdup(SMALL_FONT);
2798 g_free(last_normalfont);
2799 last_normalfont = g_strdup(NORMAL_FONT);
2800 g_free(last_boldfont);
2801 last_boldfont = g_strdup(BOLD_FONT);
2802 last_derive = prefs_common.derive_from_normal_font;
2804 #define STYLE_FREE(s) \
2806 g_object_unref(s); \
2810 STYLE_FREE(normal_style);
2811 STYLE_FREE(normal_color_style);
2812 STYLE_FREE(bold_style);
2813 STYLE_FREE(bold_color_style);
2814 STYLE_FREE(bold_tgtfold_style);
2818 folderview_init(folderview);
2819 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2820 folderview_column_set_titles(folderview);
2821 folderview_set_all();
2823 g_signal_handlers_block_by_func
2824 (G_OBJECT(folderview->ctree),
2825 G_CALLBACK(folderview_selected), folderview);
2828 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2829 GTK_CMCTREE(folderview->ctree), NULL, item);
2831 folderview_select(folderview, item);
2832 folderview->open_folder = FALSE;
2833 folderview->selected = node;
2836 g_signal_handlers_unblock_by_func
2837 (G_OBJECT(folderview->ctree),
2838 G_CALLBACK(folderview_selected), folderview);
2840 pos = gtk_scrolled_window_get_vadjustment(
2841 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2842 gtk_adjustment_set_value(pos, height);
2843 gtk_adjustment_changed(pos);
2844 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2847 static void drag_state_stop(FolderView *folderview)
2849 if (folderview->drag_timer_id)
2850 g_source_remove(folderview->drag_timer_id);
2851 folderview->drag_timer_id = 0;
2852 folderview->drag_node = NULL;
2855 static gboolean folderview_defer_expand(FolderView *folderview)
2857 if (folderview->drag_node) {
2858 folderview_recollapse_nodes(folderview, folderview->drag_node);
2859 if (folderview->drag_item->collapsed) {
2860 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2861 folderview->nodes_to_recollapse = g_slist_append
2862 (folderview->nodes_to_recollapse, folderview->drag_node);
2865 folderview->drag_item = NULL;
2866 folderview->drag_timer_id = 0;
2870 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2872 /* the idea is that we call drag_state_start() whenever we want expansion to
2873 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2874 * we need to call drag_state_stop() */
2875 drag_state_stop(folderview);
2876 /* request expansion */
2877 if (0 != (folderview->drag_timer_id = g_timeout_add
2878 (prefs_common.hover_timeout,
2879 (GSourceFunc)folderview_defer_expand,
2881 folderview->drag_node = node;
2882 folderview->drag_item = item;
2885 #ifndef GENERIC_UMPC
2886 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2887 FolderView *folderview)
2889 GdkDragContext *context;
2891 cm_return_if_fail(folderview != NULL);
2892 if (folderview->selected == NULL) return;
2893 if (folderview->nodes_to_recollapse)
2894 g_slist_free(folderview->nodes_to_recollapse);
2895 folderview->nodes_to_recollapse = NULL;
2896 context = gtk_drag_begin(widget, folderview->target_list,
2897 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2898 gtk_drag_set_icon_default(context);
2901 static void folderview_drag_data_get(GtkWidget *widget,
2902 GdkDragContext *drag_context,
2903 GtkSelectionData *selection_data,
2906 FolderView *folderview)
2910 gchar *source = NULL;
2911 if (info == TARGET_DUMMY) {
2912 sel = GTK_CMCLIST(folderview->ctree)->selection;
2916 item = gtk_cmctree_node_get_row_data
2917 (GTK_CMCTREE(folderview->ctree),
2918 GTK_CMCTREE_NODE(sel->data));
2920 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2921 gtk_selection_data_set(selection_data,
2922 gtk_selection_data_get_target(selection_data), 8,
2923 source, strlen(source));
2926 g_warning("unknown info %d", info);
2930 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2932 FolderUpdateData *hookdata;
2933 FolderView *folderview;
2937 folderview = (FolderView *) userdata;
2938 cm_return_val_if_fail(hookdata != NULL, FALSE);
2939 cm_return_val_if_fail(folderview != NULL, FALSE);
2941 ctree = folderview->ctree;
2942 cm_return_val_if_fail(ctree != NULL, FALSE);
2944 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2945 folderview_create_folder_node(folderview, hookdata->item);
2946 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2947 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2948 NULL, folder_item_parent(hookdata->item));
2949 folderview_sort_folders(folderview, node, hookdata->folder);
2950 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2951 GtkCMCTreeNode *node;
2953 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2955 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2956 if (folderview->selected == node)
2957 folderview->selected = NULL;
2958 if (folderview->opened == node)
2959 folderview->opened = NULL;
2961 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2962 /* do nothing, it's done by the ADD and REMOVE) */
2963 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2964 folderview_set(folderview);
2969 static gboolean folderview_dnd_scroll_cb(gpointer data)
2971 FolderView *folderview = (FolderView *)data;
2972 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2973 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2974 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2975 gint max = (int)gtk_adjustment_get_upper(pos) -
2976 (int)gtk_adjustment_get_page_size(pos);
2978 if (folderview->scroll_value == 0) {
2979 folderview->scroll_timeout_id = 0;
2983 if (folderview->scroll_value > 0 && new_val > max) {
2985 } else if (folderview->scroll_value < 0 && new_val < 0) {
2988 gtk_adjustment_set_value(pos, new_val);
2993 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2994 GdkDragContext *context,
2998 FolderView *folderview)
3001 FolderItem *item = NULL, *src_item = NULL;
3002 GtkCMCTreeNode *node = NULL;
3003 gboolean acceptable = FALSE;
3004 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3005 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3006 int height = (int)gtk_adjustment_get_page_size(pos);
3007 int total_height = (int)gtk_adjustment_get_upper(pos);
3008 int vpos = (int)gtk_adjustment_get_value(pos);
3009 int offset = prefs_common.show_col_headers ? 24:0;
3012 if (gtk_cmclist_get_selection_info
3013 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3014 GtkWidget *srcwidget;
3016 if (y > height - (48 - offset) && height + vpos < total_height) {
3017 dist = -(height - (48 - offset) - y);
3018 folderview->scroll_value = 1.41f * (1+(dist / 6));
3019 } else if (y < 72 - (24 - offset) && y >= 0) {
3020 dist = 72 - (24 - offset) - y;
3021 folderview->scroll_value = -1.41f * (1+(dist / 6));
3023 folderview->scroll_value = 0;
3025 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3026 folderview->scroll_timeout_id =
3027 g_timeout_add(30, folderview_dnd_scroll_cb,
3031 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3032 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3033 src_item = folderview->summaryview->folder_item;
3035 srcwidget = gtk_drag_get_source_widget(context);
3036 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3037 /* comes from summaryview */
3038 /* we are copying messages, so only accept folder items that are not
3039 the source item, are no root items and can copy messages */
3040 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3041 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3042 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3044 } else if (srcwidget == folderview->ctree) {
3045 /* comes from folderview */
3046 /* we are moving folder items, only accept folders that are not
3047 the source items and can copy messages and create folder items */
3048 if (item && item->folder && src_item && src_item != item &&
3049 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3050 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3051 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3052 || item->folder == src_item->folder))
3055 /* comes from another app */
3056 /* we are adding messages, so only accept folder items that are
3057 no root items and can copy messages */
3058 if (item && item->folder && folder_item_parent(item) != NULL
3059 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3060 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3065 if (acceptable || (src_item && src_item == item))
3066 drag_state_start(folderview, node, item);
3069 g_signal_handlers_block_by_func
3071 G_CALLBACK(folderview_selected), folderview);
3072 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3073 g_signal_handlers_unblock_by_func
3075 G_CALLBACK(folderview_selected), folderview);
3076 gdk_drag_status(context,
3077 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3078 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3080 if (folderview->opened)
3081 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3082 gdk_drag_status(context, 0, time);
3088 static void folderview_drag_leave_cb(GtkWidget *widget,
3089 GdkDragContext *context,
3091 FolderView *folderview)
3093 drag_state_stop(folderview);
3094 folderview->scroll_value = 0;
3095 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3098 static void free_info (gpointer stuff, gpointer data)
3103 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3104 guint time, FolderItem *item)
3107 GSList *msglist = NULL;
3108 list = uri_list_extract_filenames(data);
3109 if (!(item && item->folder && folder_item_parent(item) != NULL
3110 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3112 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3113 debug_print("item doesn't fit\n");
3117 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3118 debug_print("list is empty\n");
3121 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3122 MsgFileInfo *info = NULL;
3124 if (file_is_email((gchar *)tmp->data)) {
3125 info = g_new0(MsgFileInfo, 1);
3126 info->msginfo = NULL;
3127 info->file = (gchar *)tmp->data;
3128 msglist = g_slist_prepend(msglist, info);
3129 debug_print("file is a mail\n");
3131 debug_print("file isn't a mail\n");
3135 msglist = g_slist_reverse(msglist);
3136 folder_item_add_msgs(item, msglist, FALSE);
3137 g_slist_foreach(msglist, free_info, NULL);
3138 g_slist_free(msglist);
3139 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3141 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3143 list_free_strings(list);
3147 static void folderview_drag_received_cb(GtkWidget *widget,
3148 GdkDragContext *drag_context,
3151 GtkSelectionData *data,
3154 FolderView *folderview)
3157 FolderItem *item = NULL, *src_item;
3158 GtkCMCTreeNode *node;
3159 int offset = prefs_common.show_col_headers ? 24:0;
3161 folderview->scroll_value = 0;
3163 if (info == TARGET_DUMMY) {
3164 drag_state_stop(folderview);
3165 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3166 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3167 /* comes from summaryview */
3168 if (gtk_cmclist_get_selection_info
3169 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3172 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3173 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3174 src_item = folderview->summaryview->folder_item;
3176 if (item->no_select) {
3177 alertpanel_error(_("The destination folder can only be used to "
3178 "store subfolders."));
3181 /* re-check (due to acceptable possibly set for folder moves */
3182 if (!(item && item->folder && item->path && !item->no_select &&
3183 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3186 if (item && src_item) {
3187 switch (gdk_drag_context_get_selected_action(drag_context)) {
3188 case GDK_ACTION_COPY:
3189 summary_copy_selected_to(folderview->summaryview, item);
3190 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3192 case GDK_ACTION_MOVE:
3193 case GDK_ACTION_DEFAULT:
3195 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3196 summary_copy_selected_to(folderview->summaryview, item);
3198 summary_move_selected_to(folderview->summaryview, item);
3199 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3202 gtk_drag_finish(drag_context, FALSE, FALSE, 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);