2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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/>.
23 #include <glib/gi18n.h>
24 #include <gdk/gdkkeysyms.h>
31 #include "mainwindow.h"
32 #include "folderview.h"
33 #include "summaryview.h"
34 #include "summary_search.h"
35 #include "inputdialog.h"
36 #include "manage_window.h"
37 #include "alertpanel.h"
39 #include "stock_pixmap.h"
43 #include "prefs_common.h"
44 #include "prefs_account.h"
45 #include "prefs_filtering.h"
46 #include "prefs_folder_item.h"
49 #include "foldersel.h"
51 #include "statusbar.h"
53 #include "folderutils.h"
54 #include "partial_download.h"
55 #include "prefs_folder_column.h"
56 #include "filtering.h"
57 #include "quicksearch.h"
61 #include "gtkcmctree.h"
63 #define COL_FOLDER_WIDTH 150
64 #define COL_NUM_WIDTH 32
66 static GList *folderview_list = NULL;
68 static GtkStyle *normal_style;
69 static GtkStyle *normal_color_style;
70 static GtkStyle *bold_style;
71 static GtkStyle *bold_color_style;
72 static GtkStyle *bold_tgtfold_style;
74 static GdkPixbuf *inboxxpm;
75 static GdkPixbuf *inboxhrmxpm;
76 static GdkPixbuf *inboxopenxpm;
77 static GdkPixbuf *inboxopenhrmxpm;
78 static GdkPixbuf *outboxxpm;
79 static GdkPixbuf *outboxhrmxpm;
80 static GdkPixbuf *outboxopenxpm;
81 static GdkPixbuf *outboxopenhrmxpm;
82 static GdkPixbuf *folderxpm;
83 static GdkPixbuf *folderhrmxpm;
84 static GdkPixbuf *folderopenxpm;
85 static GdkPixbuf *folderopenhrmxpm;
86 static GdkPixbuf *trashopenxpm;
87 static GdkPixbuf *trashopenhrmxpm;
88 static GdkPixbuf *trashxpm;
89 static GdkPixbuf *trashhrmxpm;
90 static GdkPixbuf *queuexpm;
91 static GdkPixbuf *queuehrmxpm;
92 static GdkPixbuf *queueopenxpm;
93 static GdkPixbuf *queueopenhrmxpm;
94 static GdkPixbuf *draftsxpm;
95 static GdkPixbuf *draftsopenxpm;
96 static GdkPixbuf *foldersubsxpm;
97 static GdkPixbuf *foldersubsopenxpm;
98 static GdkPixbuf *foldernoselectxpm;
99 static GdkPixbuf *foldernoselectopenxpm;
101 static GdkPixbuf *m_inboxxpm;
102 static GdkPixbuf *m_inboxhrmxpm;
103 static GdkPixbuf *m_inboxopenxpm;
104 static GdkPixbuf *m_inboxopenhrmxpm;
105 static GdkPixbuf *m_outboxxpm;
106 static GdkPixbuf *m_outboxhrmxpm;
107 static GdkPixbuf *m_outboxopenxpm;
108 static GdkPixbuf *m_outboxopenhrmxpm;
109 static GdkPixbuf *m_folderxpm;
110 static GdkPixbuf *m_folderhrmxpm;
111 static GdkPixbuf *m_folderopenxpm;
112 static GdkPixbuf *m_folderopenhrmxpm;
113 static GdkPixbuf *m_trashopenxpm;
114 static GdkPixbuf *m_trashopenhrmxpm;
115 static GdkPixbuf *m_trashxpm;
116 static GdkPixbuf *m_trashhrmxpm;
117 static GdkPixbuf *m_queuexpm;
118 static GdkPixbuf *m_queuehrmxpm;
119 static GdkPixbuf *m_queueopenxpm;
120 static GdkPixbuf *m_queueopenhrmxpm;
121 static GdkPixbuf *m_draftsxpm;
122 static GdkPixbuf *m_draftsopenxpm;
123 static GdkPixbuf *m_foldersubsxpm;
124 static GdkPixbuf *m_foldernoselectxpm;
126 static GdkPixbuf *newxpm;
127 static GdkPixbuf *unreadxpm;
128 static GdkPixbuf *readxpm;
130 static void folderview_select_node (FolderView *folderview,
131 GtkCMCTreeNode *node);
132 static void folderview_set_folders (FolderView *folderview);
133 static void folderview_sort_folders (FolderView *folderview,
134 GtkCMCTreeNode *root,
136 static void folderview_append_folder (FolderView *folderview,
138 static void folderview_update_node (FolderView *folderview,
139 GtkCMCTreeNode *node);
141 static gint folderview_clist_compare (GtkCMCList *clist,
145 /* callback functions */
146 static gboolean folderview_button_pressed (GtkWidget *ctree,
147 GdkEventButton *event,
148 FolderView *folderview);
149 static gboolean folderview_button_released (GtkWidget *ctree,
150 GdkEventButton *event,
151 FolderView *folderview);
152 static gboolean folderview_key_pressed (GtkWidget *widget,
154 FolderView *folderview);
155 static void folderview_selected (GtkCMCTree *ctree,
158 FolderView *folderview);
159 static void folderview_tree_expanded (GtkCMCTree *ctree,
160 GtkCMCTreeNode *node,
161 FolderView *folderview);
162 static void folderview_tree_collapsed (GtkCMCTree *ctree,
163 GtkCMCTreeNode *node,
164 FolderView *folderview);
165 static void folderview_popup_close (GtkMenuShell *menu_shell,
166 FolderView *folderview);
167 static void folderview_col_resized (GtkCMCList *clist,
170 FolderView *folderview);
172 static void mark_all_read_unread_handler (GtkAction *action,
177 static void mark_all_read_cb (GtkAction *action,
179 static void mark_all_unread_cb (GtkAction *action,
181 static void mark_all_read_recursive_cb (GtkAction *action,
183 static void mark_all_unread_recursive_cb (GtkAction *action,
186 static void folderview_empty_trash_cb (GtkAction *action,
189 static void folderview_send_queue_cb (GtkAction *action,
192 static void folderview_search_cb (GtkAction *action,
194 static void folderview_run_processing_cb(GtkAction *action,
197 static void folderview_property_cb (GtkAction *action,
200 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
201 GdkDragContext *context,
205 FolderView *folderview);
206 static void folderview_drag_leave_cb (GtkWidget *widget,
207 GdkDragContext *context,
209 FolderView *folderview);
210 static void folderview_drag_received_cb (GtkWidget *widget,
211 GdkDragContext *drag_context,
214 GtkSelectionData *data,
217 FolderView *folderview);
219 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
220 FolderView *folderview);
222 static void folderview_drag_data_get (GtkWidget *widget,
223 GdkDragContext *drag_context,
224 GtkSelectionData *selection_data,
227 FolderView *folderview);
228 static void folderview_drag_end_cb (GtkWidget *widget,
229 GdkDragContext *drag_context,
230 FolderView *folderview);
232 static void folderview_create_folder_node (FolderView *folderview,
234 static gboolean folderview_update_folder (gpointer source,
236 static gboolean folderview_update_item_claws (gpointer source,
238 static void folderview_processing_cb(GtkAction *action, gpointer data);
239 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
240 GdkEventButton *event);
242 GHashTable *folderview_popups;
244 static GtkActionEntry folderview_common_popup_entries[] =
246 {"FolderViewPopup", NULL, "FolderViewPopup" },
247 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
248 {"FolderViewPopup/MarkAllUnread", NULL, N_("Mark all u_nread"), NULL, NULL, G_CALLBACK(mark_all_unread_cb) },
249 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
250 {"FolderViewPopup/MarkAllUnreadRec", NULL, N_("Mark all unread recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_unread_recursive_cb) },
251 {"FolderViewPopup/---", NULL, "---" },
252 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
253 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
254 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
255 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
256 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
257 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
261 GtkTargetEntry folderview_drag_types[] =
263 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
264 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
267 void folderview_initialize(void)
269 FolderViewPopup *fpopup;
271 fpopup = g_new0(FolderViewPopup, 1);
273 fpopup->klass = "common";
274 fpopup->path = "<CommonFolder>";
275 fpopup->entries = folderview_common_popup_entries;
276 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
277 fpopup->set_sensitivity = NULL;
279 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
280 g_hash_table_insert(folderview_popups, "common", fpopup);
283 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
285 FolderViewPopup *fpopup_common;
286 GtkActionGroup *action_group;
288 action_group = cm_menu_create_action_group(
290 fpopup->entries, fpopup->n_entries,
291 (gpointer)folderview);
293 if (fpopup->toggle_entries)
294 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
295 fpopup->n_toggle_entries,
296 (gpointer)folderview);
297 if (fpopup->radio_entries)
298 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
299 fpopup->n_radio_entries, fpopup->radio_default,
300 G_CALLBACK(fpopup->radio_callback),
301 (gpointer)folderview);
303 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
304 if (fpopup_common != fpopup) {
305 gtk_action_group_add_actions(action_group, fpopup_common->entries,
306 fpopup_common->n_entries,
307 (gpointer)folderview);
308 if (fpopup_common->toggle_entries)
309 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
310 fpopup_common->n_toggle_entries,
311 (gpointer)folderview);
312 if (fpopup_common->radio_entries)
313 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
314 fpopup_common->n_radio_entries, fpopup_common->radio_default,
315 G_CALLBACK(fpopup_common->radio_callback),
316 (gpointer)folderview);
322 static void create_action_groups(gpointer key, gpointer value, gpointer data)
324 FolderView *folderview = data;
325 FolderViewPopup *fpopup = value;
326 GtkActionGroup *group;
328 group = create_action_group(folderview, fpopup);
329 g_hash_table_insert(folderview->popups, fpopup->klass, group);
332 static void folderview_column_set_titles(FolderView *folderview)
334 GtkWidget *ctree = folderview->ctree;
335 GtkWidget *label_folder;
336 GtkWidget *label_new;
337 GtkWidget *label_unread;
338 GtkWidget *label_total;
339 GtkWidget *hbox_folder;
341 GtkWidget *hbox_unread;
342 GtkWidget *hbox_total;
343 gint *col_pos = folderview->col_pos;
345 debug_print("setting titles...\n");
346 gtk_widget_realize(folderview->ctree);
347 gtk_widget_show_all(folderview->scrolledwin);
349 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
350 * instead text (text overflows making them unreadable and ugly) */
351 stock_pixbuf_gdk(STOCK_PIXMAP_NEW, &newxpm);
352 stock_pixbuf_gdk(STOCK_PIXMAP_UNREAD, &unreadxpm);
353 stock_pixbuf_gdk(STOCK_PIXMAP_READ, &readxpm);
355 label_folder = gtk_label_new(_("Folder"));
356 label_new = gtk_image_new_from_pixbuf(newxpm);
357 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
358 label_total = gtk_image_new_from_pixbuf(readxpm);
360 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
362 hbox_folder = gtk_hbox_new(FALSE, 4);
363 hbox_new = gtk_hbox_new(FALSE, 4);
364 hbox_unread = gtk_hbox_new(FALSE, 4);
365 hbox_total = gtk_hbox_new(FALSE, 4);
368 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
369 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
370 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
371 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
372 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
373 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
374 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
375 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
377 gtk_widget_show_all(hbox_folder);
378 gtk_widget_show_all(hbox_new);
379 gtk_widget_show_all(hbox_unread);
380 gtk_widget_show_all(hbox_total);
383 gtk_widget_set_size_request(hbox_new, -1, 20);
384 gtk_widget_set_size_request(hbox_unread, -1, 20);
385 gtk_widget_set_size_request(hbox_total, -1, 20);
388 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
389 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
390 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
391 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
397 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
398 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
399 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
402 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
404 FolderView *folderview = (FolderView *)data;
405 GdkEventButton event;
406 if (folderview_get_selected_item(folderview) == NULL)
410 event.time = gtk_get_current_event_time();
412 folderview_set_sens_and_popup_menu(folderview, -1,
419 static GtkWidget *folderview_ctree_create(FolderView *folderview)
423 FolderColumnState *col_state;
424 FolderColumnType type;
425 gchar *titles[N_FOLDER_COLS];
427 GtkWidget *scrolledwin = folderview->scrolledwin;
429 debug_print("creating tree...\n");
430 memset(titles, 0, sizeof(titles));
432 col_state = prefs_folder_column_get_config();
433 memset(titles, 0, sizeof(titles));
435 col_pos = folderview->col_pos;
437 for (i = 0; i < N_FOLDER_COLS; i++) {
438 folderview->col_state[i] = col_state[i];
439 type = col_state[i].type;
443 titles[col_pos[F_COL_FOLDER]] = _("Folder");
444 titles[col_pos[F_COL_NEW]] = _("New");
445 titles[col_pos[F_COL_UNREAD]] = _("Unread");
446 /* TRANSLATORS: This in Number sign in American style */
447 titles[col_pos[F_COL_TOTAL]] = _("#");
449 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
452 if (prefs_common.show_col_headers == FALSE)
453 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
456 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
457 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
459 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
460 col_pos[F_COL_UNREAD],
462 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
463 col_pos[F_COL_TOTAL],
465 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
466 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
467 GTK_CMCTREE_EXPANDER_TRIANGLE);
469 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
470 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
472 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
473 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
475 /* don't let title buttons take key focus */
476 for (i = 0; i < N_FOLDER_COLS; i++) {
477 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
478 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
479 prefs_common.folder_col_size[i]);
480 gtk_cmclist_set_column_visibility
481 (GTK_CMCLIST(ctree), i, col_state[i].visible);
484 g_signal_connect(G_OBJECT(ctree), "key_press_event",
485 G_CALLBACK(folderview_key_pressed),
487 g_signal_connect(G_OBJECT(ctree), "button_press_event",
488 G_CALLBACK(folderview_button_pressed),
490 g_signal_connect(G_OBJECT(ctree), "popup-menu",
491 G_CALLBACK(folderview_popup_menu), folderview);
492 g_signal_connect(G_OBJECT(ctree), "button_release_event",
493 G_CALLBACK(folderview_button_released),
495 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
496 G_CALLBACK(folderview_selected), folderview);
498 /* drag-n-dropping folders on maemo is impractical as this
499 * opens the folder almost everytime */
500 g_signal_connect(G_OBJECT(ctree), "start_drag",
501 G_CALLBACK(folderview_start_drag), folderview);
503 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
504 G_CALLBACK(folderview_drag_data_get),
507 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
508 G_CALLBACK(folderview_tree_expanded),
510 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
511 G_CALLBACK(folderview_tree_collapsed),
514 g_signal_connect(G_OBJECT(ctree), "resize_column",
515 G_CALLBACK(folderview_col_resized),
519 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
520 folderview_drag_types, 2,
521 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
522 g_signal_connect(G_OBJECT(ctree), "drag_motion",
523 G_CALLBACK(folderview_drag_motion_cb),
525 g_signal_connect(G_OBJECT(ctree), "drag_leave",
526 G_CALLBACK(folderview_drag_leave_cb),
528 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
529 G_CALLBACK(folderview_drag_received_cb),
531 g_signal_connect(G_OBJECT(ctree), "drag_end",
532 G_CALLBACK(folderview_drag_end_cb),
535 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
540 void folderview_set_column_order(FolderView *folderview)
542 GtkWidget *ctree = folderview->ctree;
543 FolderItem *item = folderview_get_selected_item(folderview);
544 FolderItem *sel_item = NULL, *op_item = NULL;
545 GtkWidget *scrolledwin = folderview->scrolledwin;
547 if (folderview->drag_timer_id != 0) {
548 g_source_remove(folderview->drag_timer_id);
549 folderview->drag_timer_id = 0;
551 if (folderview->deferred_refresh_id != 0) {
552 g_source_remove(folderview->deferred_refresh_id);
553 folderview->deferred_refresh_id = 0;
555 if (folderview->scroll_timeout_id != 0) {
556 g_source_remove(folderview->scroll_timeout_id);
557 folderview->scroll_timeout_id = 0;
559 if (folderview->postpone_select_id != 0) {
560 g_source_remove(folderview->postpone_select_id);
561 folderview->postpone_select_id = 0;
564 if (folderview->selected)
565 sel_item = folderview_get_selected_item(folderview);
566 if (folderview->opened)
567 op_item = folderview_get_opened_item(folderview);
569 debug_print("recreating tree...\n");
570 gtk_widget_destroy(folderview->ctree);
573 folderview->ctree = ctree = folderview_ctree_create(folderview);
574 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
575 GTK_CMCLIST(ctree)->hadjustment);
576 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
577 GTK_CMCLIST(ctree)->vadjustment);
578 gtk_widget_show(ctree);
581 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
583 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
585 folderview_set(folderview);
586 folderview_column_set_titles(folderview);
588 folderview_select(folderview,item);
591 FolderView *folderview_create(void)
593 FolderView *folderview;
594 GtkWidget *scrolledwin;
597 debug_print("Creating folder view...\n");
598 folderview = g_new0(FolderView, 1);
600 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
601 gtk_scrolled_window_set_policy
602 (GTK_SCROLLED_WINDOW(scrolledwin),
603 GTK_POLICY_AUTOMATIC,
604 prefs_common.folderview_vscrollbar_policy);
605 gtk_widget_set_size_request(scrolledwin,
606 prefs_common.folderview_width,
607 prefs_common.folderview_height);
609 folderview->scrolledwin = scrolledwin;
610 ctree = folderview_ctree_create(folderview);
612 /* create popup factories */
613 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
614 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
616 folderview->ctree = ctree;
618 folderview->folder_update_callback_id =
619 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
620 folderview->folder_item_update_callback_id =
621 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
623 gtk_widget_show_all(scrolledwin);
625 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
626 folderview_list = g_list_append(folderview_list, folderview);
628 folderview->drag_timer_id = 0;
629 folderview->deferred_refresh_id = 0;
630 folderview->scroll_timeout_id = 0;
631 folderview->postpone_select_id = 0;
636 void folderview_init(FolderView *folderview)
638 GtkWidget *ctree = folderview->ctree;
640 PangoFontDescription *normal_font;
642 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
643 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
644 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
645 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
646 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
647 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
648 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
649 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
650 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
651 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
652 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
653 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
654 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
655 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
656 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
657 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
658 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
659 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
660 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
661 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
662 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
663 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
664 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_OPEN, &foldersubsopenxpm);
665 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE, &foldersubsxpm);
666 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_OPEN, &foldernoselectopenxpm);
667 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE, &foldernoselectxpm);
669 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
670 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
671 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
672 stock_pixbuf_gdk(STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
673 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
674 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
675 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
676 stock_pixbuf_gdk(STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
677 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
678 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
679 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
680 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
681 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
682 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
683 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
684 stock_pixbuf_gdk(STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
685 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
686 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
687 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
688 stock_pixbuf_gdk(STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
689 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
690 stock_pixbuf_gdk(STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
691 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_SUBS_CLOSE_MARK, &m_foldersubsxpm);
692 stock_pixbuf_gdk(STOCK_PIXMAP_DIR_NOSELECT_CLOSE_MARK, &m_foldernoselectxpm);
694 normal_font = pango_font_description_from_string(NORMAL_FONT);
696 gtk_widget_modify_font(ctree, normal_font);
697 pango_font_description_free(normal_font);
699 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
702 PangoFontDescription *font_desc;
703 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
704 font_desc = pango_font_description_from_string(NORMAL_FONT);
706 if (normal_style->font_desc)
707 pango_font_description_free
708 (normal_style->font_desc);
709 normal_style->font_desc = font_desc;
711 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
712 normal_color_style = gtk_style_copy(normal_style);
713 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
717 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
718 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
719 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
720 PangoFontDescription *font_desc;
721 font_desc = pango_font_description_from_string(NORMAL_FONT);
723 pango_font_description_free(bold_style->font_desc);
724 bold_style->font_desc = font_desc;
726 pango_font_description_set_weight
727 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
729 PangoFontDescription *font_desc;
730 font_desc = pango_font_description_from_string(BOLD_FONT);
732 if (bold_style->font_desc)
733 pango_font_description_free
734 (bold_style->font_desc);
735 bold_style->font_desc = font_desc;
738 bold_color_style = gtk_style_copy(bold_style);
739 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
741 bold_tgtfold_style = gtk_style_copy(bold_style);
742 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
746 static gboolean folderview_defer_set(gpointer data)
748 FolderView *folderview = (FolderView *)data;
749 MainWindow *mainwin = folderview->mainwin;
753 if (mainwin->lock_count)
756 debug_print("doing deferred folderview_set now\n");
757 folderview_set(folderview);
759 folderview->deferred_refresh_id = 0;
763 void folderview_set(FolderView *folderview)
765 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
766 MainWindow *mainwin = folderview->mainwin;
767 FolderItem *sel_item = NULL, *op_item = NULL;
772 if (mainwin->lock_count) {
773 if (folderview->deferred_refresh_id == 0)
774 folderview->deferred_refresh_id =
775 g_timeout_add(500, folderview_defer_set, folderview);
776 debug_print("deferred folderview_set\n");
781 debug_print("Setting folder info...\n");
782 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
784 main_window_cursor_wait(mainwin);
786 if (folderview->selected)
787 sel_item = folderview_get_selected_item(folderview);
788 if (folderview->opened)
789 op_item = folderview_get_opened_item(folderview);
791 folderview->selected = NULL;
792 folderview->opened = NULL;
794 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
795 gtk_cmclist_clear(GTK_CMCLIST(ctree));
797 folderview_set_folders(folderview);
800 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
802 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
804 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
805 main_window_cursor_normal(mainwin);
806 STATUSBAR_POP(mainwin);
810 void folderview_set_all(void)
814 for (list = folderview_list; list != NULL; list = list->next)
815 folderview_set((FolderView *)list->data);
818 void folderview_select(FolderView *folderview, FolderItem *item)
820 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
821 GtkCMCTreeNode *node;
822 GtkCMCTreeNode *old_selected = folderview->selected;
826 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
827 if (node) folderview_select_node(folderview, node);
829 if (old_selected != node)
830 folder_update_op_count();
833 static void mark_all_read_cb(GtkAction *action, gpointer data)
835 mark_all_read_unread_handler(action, data, FALSE, TRUE);
838 static void mark_all_unread_cb(GtkAction *action, gpointer data)
840 mark_all_read_unread_handler(action, data, FALSE, FALSE);
843 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
845 mark_all_read_unread_handler(action, data, TRUE, TRUE);
848 static void mark_all_unread_recursive_cb(GtkAction *action, gpointer data)
850 mark_all_read_unread_handler(action, data, TRUE, FALSE);
853 static void mark_all_read_unread_handler(GtkAction *action, gpointer data,
854 gboolean recursive, gboolean read)
856 FolderView *folderview = (FolderView *)data;
862 item = folderview_get_selected_item(folderview);
867 title = _("Mark all as read");
868 message = recursive? _("Do you really want to mark all mails in this "
869 "folder and its sub-folders as read?") :
870 _("Do you really want to mark all mails in this "
873 title = _("Mark all as unread");
874 message = recursive? _("Do you really want to mark all mails in this "
875 "folder and its sub-folders as unread?") :
876 _("Do you really want to mark all mails in this "
877 "folder as unread?");
879 if (folderview->summaryview->folder_item != item &&
880 prefs_common.ask_mark_all_read) {
881 val = alertpanel_full(title, message,
882 GTK_STOCK_NO, GTK_STOCK_YES, NULL,
883 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
885 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
887 else if (val & G_ALERTDISABLE)
888 prefs_common.ask_mark_all_read = FALSE;
891 folder_item_update_freeze();
892 if (folderview->summaryview->folder_item != item && !recursive)
893 summary_lock(folderview->summaryview);
895 summary_freeze(folderview->summaryview);
899 folderutils_mark_all_read_recursive(item);
901 folderutils_mark_all_read(item);
904 folderutils_mark_all_unread_recursive(item);
906 folderutils_mark_all_unread(item);
908 if (folderview->summaryview->folder_item != item && !recursive)
909 summary_unlock(folderview->summaryview);
911 summary_thaw(folderview->summaryview);
912 folder_item_update_thaw();
915 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
917 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
919 cm_return_if_fail(node != NULL);
921 if (folderview->open_folder) {
925 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
926 gtkut_ctree_expand_parent_all(ctree, node);
928 folderview->open_folder = TRUE;
929 gtkut_ctree_set_focus_row(ctree, node);
930 gtk_cmctree_select(ctree, node);
931 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
932 if ((folderview->summaryview->folder_item &&
933 folderview->summaryview->folder_item->total_msgs > 0) ||
934 prefs_common.layout_mode == SMALL_LAYOUT)
935 summary_select_node(folderview->summaryview,
936 folderview->summaryview->selected, -1);
938 gtk_widget_grab_focus(folderview->ctree);
941 void folderview_unselect(FolderView *folderview)
943 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
945 (GTK_CMCTREE(folderview->ctree), folderview->opened);
947 folderview->selected = folderview->opened = NULL;
950 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
951 GtkCMCTreeNode *node,
957 node = gtkut_ctree_node_next(ctree, node);
959 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
961 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
962 item = gtk_cmctree_node_get_row_data(ctree, node);
965 if (item->stype == F_TRASH || item->stype == F_DRAFT)
969 if(item->unread_msgs > 0)
973 if(item->new_msgs > 0)
977 if(item->marked_msgs > 0)
981 if(item->total_msgs > 0)
990 void folderview_select_next_with_flag(FolderView *folderview,
993 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
994 GtkCMCTreeNode *node = NULL;
995 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
999 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
1002 prefs_common.summary_select_prio[0] = ACTION_NEW;
1005 prefs_common.summary_select_prio[0] = ACTION_MARKED;
1008 prefs_common.summary_select_prio[0] = ACTION_FIRST_LIST;
1012 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1014 folderview_select_node(folderview, node);
1018 if (!folderview->opened ||
1019 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1023 /* search again from the first node */
1024 node = folderview_find_next_with_flag(ctree, NULL, flag);
1026 folderview_select_node(folderview, node);
1029 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1032 FolderItem *folderview_get_selected_item(FolderView *folderview)
1034 g_return_val_if_fail(folderview != NULL, NULL);
1035 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1037 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1039 if (!folderview->selected) return NULL;
1040 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1043 FolderItem *folderview_get_opened_item(FolderView *folderview)
1045 g_return_val_if_fail(folderview != NULL, NULL);
1046 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1048 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1050 if (!folderview->opened) return NULL;
1051 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1054 static void folderview_set_folders(FolderView *folderview)
1057 list = folder_get_list();
1059 for (; list != NULL; list = list->next) {
1060 folderview_append_folder(folderview, FOLDER(list->data));
1064 static gchar *get_scan_str(FolderItem *item)
1067 return g_strdup_printf(_("Scanning folder %s/%s..."),
1068 item->folder->name, item->path);
1070 return g_strdup_printf(_("Scanning folder %s..."),
1071 item->folder->name);
1073 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1077 for (list = folderview_list; list != NULL; list = list->next) {
1078 FolderView *folderview = (FolderView *)list->data;
1079 MainWindow *mainwin = folderview->mainwin;
1080 gchar *str = get_scan_str(item);
1082 STATUSBAR_PUSH(mainwin, str);
1083 STATUSBAR_POP(mainwin);
1088 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1091 MainWindow *mainwin = mainwindow_get_mainwindow();
1092 FolderView *folderview = NULL;
1093 GtkAdjustment *pos = NULL;
1096 cm_return_if_fail(folder != NULL);
1098 if (!folder->klass->scan_tree) return;
1101 alertpanel_full(_("Rebuild folder tree"),
1102 _("Rebuilding the folder tree will remove "
1103 "local caches. Do you want to continue?"),
1104 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1105 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1106 != G_ALERTALTERNATE) {
1112 window = label_window_create(_("Rebuilding folder tree..."));
1114 window = label_window_create(_("Scanning folder tree..."));
1117 folderview = mainwin->folderview;
1120 pos = gtk_scrolled_window_get_vadjustment(
1121 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1122 height = gtk_adjustment_get_value(pos);
1125 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1126 folder_scan_tree(folder, rebuild);
1127 folder_set_ui_func(folder, NULL, NULL);
1129 folderview_set_all();
1132 pos = gtk_scrolled_window_get_vadjustment(
1133 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1134 gtk_adjustment_set_value(pos, height);
1135 gtk_adjustment_changed(pos);
1137 label_window_destroy(window);
1141 /** folderview_check_new()
1142 * Scan and update the folder and return the
1143 * count the number of new messages since last check.
1144 * \param folder the folder to check for new messages
1145 * \return the number of new messages since last check
1147 gint folderview_check_new(Folder *folder)
1151 FolderView *folderview;
1153 GtkCMCTreeNode *node;
1155 gint former_new_msgs = 0;
1156 gint former_new = 0, former_unread = 0, former_total;
1158 for (list = folderview_list; list != NULL; list = list->next) {
1159 folderview = (FolderView *)list->data;
1160 ctree = GTK_CMCTREE(folderview->ctree);
1161 folderview->scanning_folder = folder;
1163 main_window_lock(folderview->mainwin);
1165 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1166 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1168 item = gtk_cmctree_node_get_row_data(ctree, node);
1169 if (!item || !item->path || !item->folder) continue;
1170 if (item->no_select) continue;
1171 if (folder && folder != item->folder) continue;
1172 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1173 if (!item->prefs->newmailcheck) continue;
1174 if (item->processing_pending == TRUE) {
1175 debug_print("skipping %s, processing pending\n",
1176 item->path ? item->path : item->name);
1179 if (item->scanning != ITEM_NOT_SCANNING) {
1180 debug_print("skipping %s, scanning\n",
1181 item->path ? item->path : item->name);
1185 str = get_scan_str(item);
1187 STATUSBAR_PUSH(folderview->mainwin, str);
1191 folderview_scan_tree_func(item->folder, item, NULL);
1192 former_new = item->new_msgs;
1193 former_unread = item->unread_msgs;
1194 former_total = item->total_msgs;
1196 if (item->folder->klass->scan_required &&
1197 (item->folder->klass->scan_required(item->folder, item) ||
1198 item->folder->inbox == item ||
1199 item->opened == TRUE ||
1200 item->processing_pending == TRUE)) {
1201 if (folder_item_scan(item) < 0) {
1203 summaryview_unlock(folderview->summaryview, item);
1204 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1205 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1206 item->path ? item->path:item->name);
1207 STATUSBAR_POP(folderview->mainwin);
1209 } else if (!FOLDER_IS_LOCAL(folder)) {
1210 STATUSBAR_POP(folderview->mainwin);
1215 } else if (!item->folder->klass->scan_required) {
1216 if (folder_item_scan(item) < 0) {
1217 summaryview_unlock(folderview->summaryview, item);
1218 if (folder && !FOLDER_IS_LOCAL(folder)) {
1219 STATUSBAR_POP(folderview->mainwin);
1224 if (former_new != item->new_msgs ||
1225 former_unread != item->unread_msgs ||
1226 former_total != item->total_msgs)
1227 folderview_update_node(folderview, node);
1229 new_msgs += item->new_msgs;
1230 former_new_msgs += former_new;
1231 STATUSBAR_POP(folderview->mainwin);
1233 folderview->scanning_folder = NULL;
1234 main_window_unlock(folderview->mainwin);
1238 folder_write_list();
1239 /* Number of new messages since last check is the just the difference
1240 * between former_new_msgs and new_msgs. If new_msgs is less than
1241 * former_new_msgs, that would mean another session accessed the folder
1242 * and the result is not well defined.
1244 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1248 void folderview_check_new_all(void)
1252 FolderView *folderview;
1254 folderview = (FolderView *)folderview_list->data;
1257 main_window_lock(folderview->mainwin);
1258 window = label_window_create
1259 (_("Checking for new messages in all folders..."));
1261 list = folder_get_list();
1262 for (; list != NULL; list = list->next) {
1263 Folder *folder = list->data;
1265 folderview_check_new(folder);
1268 folder_write_list();
1269 folderview_set_all();
1271 label_window_destroy(window);
1272 main_window_unlock(folderview->mainwin);
1276 static gboolean folderview_have_children_sub(FolderView *folderview,
1282 if (!item || !item->folder || !item->folder->node)
1285 node = item->folder->node;
1287 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1288 node = node->children;
1290 if (in_sub && item->total_msgs > 0) {
1294 while (node != NULL) {
1295 if (node && node->data) {
1296 FolderItem *next_item = (FolderItem*) node->data;
1298 if (folderview_have_children_sub(folderview,
1307 static gboolean folderview_have_children(FolderView *folderview,
1310 return folderview_have_children_sub(folderview, item, FALSE);
1313 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1319 if (!item || !item->folder || !item->folder->node)
1322 node = item->folder->node;
1324 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1325 node = node->children;
1328 (item->new_msgs > 0 ||
1329 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1333 while (node != NULL) {
1334 if (node && node->data) {
1335 FolderItem *next_item = (FolderItem*) node->data;
1337 if (folderview_have_new_children_sub(folderview,
1346 static gboolean folderview_have_new_children(FolderView *folderview,
1349 return folderview_have_new_children_sub(folderview, item, FALSE);
1352 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1358 if (!item || !item->folder || !item->folder->node)
1361 node = item->folder->node;
1363 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1364 node = node->children;
1367 (item->unread_msgs > 0 ||
1368 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1372 while (node != NULL) {
1373 if (node && node->data) {
1374 FolderItem *next_item = (FolderItem*) node->data;
1376 if (folderview_have_unread_children_sub(folderview,
1386 static gboolean folderview_have_unread_children(FolderView *folderview,
1389 return folderview_have_unread_children_sub(folderview, item, FALSE);
1392 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1398 if (!item || !item->folder || !item->folder->node) {
1402 node = item->folder->node;
1404 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1405 node = node->children;
1408 (((item->total_msgs > 0) &&
1409 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1413 while (node != NULL) {
1414 if (node && node->data) {
1415 FolderItem *next_item = (FolderItem*) node->data;
1417 if (folderview_have_read_children_sub(folderview,
1425 debug_print("-> false 2 %s\n", item->name);
1429 static gboolean folderview_have_read_children(FolderView *folderview,
1433 ret = folderview_have_read_children_sub(folderview, item, FALSE);
1434 debug_print("-> %s 0 (%d)\n", ret?"true":"false", ret);
1438 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1444 if (!item || !item->folder || !item->folder->node)
1447 node = item->folder->node;
1449 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1450 node = node->children;
1452 if (in_sub && item->search_match){
1456 while (node != NULL) {
1457 if (node && node->data) {
1458 FolderItem *next_item = (FolderItem*) node->data;
1460 if (folderview_have_matching_children_sub(folderview,
1470 static gboolean folderview_have_matching_children(FolderView *folderview,
1473 return folderview_have_matching_children_sub(folderview, item, FALSE);
1476 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1482 if (!item || !item->folder || !item->folder->node)
1485 node = item->folder->node;
1487 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1488 node = node->children;
1490 if (item->marked_msgs != 0) {
1494 while (node != NULL) {
1495 if (node && node->data) {
1496 FolderItem *next_item = (FolderItem*) node->data;
1498 if (folderview_have_marked_children_sub(folderview,
1507 static gboolean folderview_have_marked_children(FolderView *folderview,
1510 return folderview_have_marked_children_sub(folderview, item, FALSE);
1513 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1515 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1516 GtkStyle *style = NULL;
1517 GtkStyle *color_style = NULL;
1519 GdkPixbuf *xpm, *openxpm;
1520 static GdkPixbuf *searchicon;
1521 gboolean mark = FALSE;
1524 gboolean add_unread_mark;
1525 gboolean add_sub_match_mark;
1526 gboolean use_bold, use_color;
1527 gint *col_pos = folderview->col_pos;
1528 SpecialFolderItemType stype;
1530 item = gtk_cmctree_node_get_row_data(ctree, node);
1531 cm_return_if_fail(item != NULL);
1533 if (!GTK_CMCTREE_ROW(node)->expanded)
1534 mark = folderview_have_marked_children(folderview, item);
1536 mark = (item->marked_msgs != 0);
1538 stype = item->stype;
1539 if (stype == F_NORMAL) {
1540 if (folder_has_parent_of_type(item, F_TRASH))
1542 else if (folder_has_parent_of_type(item, F_DRAFT))
1544 else if (folder_has_parent_of_type(item, F_OUTBOX))
1546 else if (folder_has_parent_of_type(item, F_QUEUE))
1551 if (item->hide_read_msgs || item->hide_read_threads) {
1552 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1553 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1555 xpm = mark?m_inboxxpm:inboxxpm;
1556 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1560 if (item->hide_read_msgs || item->hide_read_threads) {
1561 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1562 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1564 xpm = mark?m_outboxxpm:outboxxpm;
1565 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1569 if (item->hide_read_msgs || item->hide_read_threads) {
1570 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1571 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1573 xpm = mark?m_queuexpm:queuexpm;
1574 openxpm = mark?m_queueopenxpm:queueopenxpm;
1578 if (item->hide_read_msgs || item->hide_read_threads) {
1579 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1580 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1582 xpm = mark?m_trashxpm:trashxpm;
1583 openxpm = mark?m_trashopenxpm:trashopenxpm;
1587 xpm = mark?m_draftsxpm:draftsxpm;
1588 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1592 FOLDER_TYPE(item->folder) == F_IMAP &&
1593 item->folder->account->imap_subsonly) {
1594 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1595 openxpm = foldersubsopenxpm;
1596 } else if (item->no_select) {
1597 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1598 openxpm = foldernoselectopenxpm;
1599 } else if (item->hide_read_msgs || item->hide_read_threads) {
1600 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1601 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1603 xpm = mark?m_folderxpm:folderxpm;
1604 openxpm = mark?m_folderopenxpm:folderopenxpm;
1608 name = folder_item_get_name(item);
1610 if (!GTK_CMCTREE_ROW(node)->expanded) {
1611 add_unread_mark = folderview_have_unread_children(
1613 add_sub_match_mark = folderview_have_matching_children(
1616 add_unread_mark = FALSE;
1617 add_sub_match_mark = FALSE;
1620 if (item->search_match) {
1622 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1625 xpm = openxpm = searchicon;
1629 if (prefs_common.display_folder_unread) {
1630 if (folder_has_parent_of_type(item, F_QUEUE)) {
1631 /* only total_msgs matters here */
1632 if (item->total_msgs > 0) {
1633 /* show total number (should be equal to the unread number)
1635 str = g_strdup_printf("%s (%d%s%s)",
1636 name, item->total_msgs,
1637 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1638 (item->unreadmarked_msgs > 0) ? "!" : "");
1641 if (prefs_common.display_folder_unread == 1) {
1642 if (item->unread_msgs > 0) {
1643 /* show unread number and signs */
1644 str = g_strdup_printf("%s (%d%s%s)",
1645 name, item->unread_msgs,
1646 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1647 (item->unreadmarked_msgs > 0) ? "!" : "");
1650 if (item->total_msgs > 0) {
1651 /* show unread number, total number and signs if any */
1652 str = g_strdup_printf("%s (%d/%d%s%s)",
1653 name, item->unread_msgs, item->total_msgs,
1654 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1655 (item->unreadmarked_msgs > 0) ? "!" : "");
1659 if ((str == NULL) &&
1660 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1661 /* no unread/total numbers, but at least one sign */
1662 str = g_strdup_printf("%s (%s%s)",
1664 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1665 (item->unreadmarked_msgs > 0) ? "!" : "");
1669 /* last fallback, folder name only or with +! sign */
1670 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1671 str = g_strdup_printf("%s%s",
1673 } else if (item->unreadmarked_msgs > 0) {
1674 str = g_strdup_printf("%s%s",
1676 } else if (add_sub_match_mark) {
1677 str = g_strdup_printf("%s%s",
1680 str = g_strdup_printf("%s", name);
1683 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1685 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1689 if (!folder_item_parent(item)) {
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1691 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1692 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1694 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1695 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1696 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1699 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1700 folder_has_parent_of_type(item, F_TRASH)) {
1701 use_bold = use_color = FALSE;
1702 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1703 GSList *list = folder_item_get_msg_list(item);
1705 use_bold = use_color = FALSE;
1706 for (cur = list; cur; cur = cur->next) {
1707 MsgInfo *msginfo = (MsgInfo *)cur->data;
1708 if (!MSG_IS_DELETED(msginfo->flags)) {
1709 /* highlight queue folder if there are any messages */
1710 use_bold = use_color = TRUE;
1714 if (!GTK_CMCTREE_ROW(node)->expanded &&
1715 use_bold == FALSE &&
1716 folderview_have_children(folderview, item))
1717 use_bold = use_color = TRUE;
1718 procmsg_msg_list_free(list);
1720 /* if unread messages exist, print with bold font */
1721 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1723 /* if new messages exist, print with colored letter */
1725 (item->new_msgs > 0) ||
1727 folderview_have_new_children(folderview, item));
1730 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1735 if (item->prefs->color > 0 && !use_color) {
1736 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1737 color_style = gtk_style_copy(bold_style);
1738 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1739 style = color_style;
1740 } else if (use_color) {
1741 style = bold_color_style;
1744 if (item->op_count > 0) {
1745 style = bold_tgtfold_style;
1747 } else if (use_color) {
1748 style = normal_color_style;
1749 gtk_cmctree_node_set_foreground(ctree, node,
1750 &folderview->color_new);
1751 } else if (item->op_count > 0) {
1752 style = bold_tgtfold_style;
1753 } else if (item->prefs->color > 0) {
1755 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1756 color_style = gtk_style_copy(normal_style);
1757 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1758 style = color_style;
1760 style = normal_style;
1763 gtk_cmctree_node_set_row_style(ctree, node, style);
1765 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1766 folderview_update_node(folderview, node);
1769 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1772 FolderView *folderview;
1774 GtkCMCTreeNode *node;
1776 cm_return_if_fail(item != NULL);
1778 for (list = folderview_list; list != NULL; list = list->next) {
1779 folderview = (FolderView *)list->data;
1780 ctree = GTK_CMCTREE(folderview->ctree);
1782 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1783 if (node && item->search_match != matches) {
1784 item->search_match = matches;
1785 folderview_update_node(folderview, node);
1790 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1792 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1793 FolderView *folderview = (FolderView *)data;
1795 GtkCMCTreeNode *node;
1796 cm_return_val_if_fail(update_info != NULL, TRUE);
1797 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1798 cm_return_val_if_fail(folderview != NULL, FALSE);
1800 ctree = GTK_CMCTREE(folderview->ctree);
1802 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1805 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1806 folderview_update_node(folderview, node);
1808 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1809 update_info->item == folderview->summaryview->folder_item &&
1810 update_info->item != NULL)
1811 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1812 summary_show(folderview->summaryview, update_info->item);
1818 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1819 GNode *gnode, GtkCMCTreeNode *cnode,
1822 FolderView *folderview = (FolderView *)data;
1823 FolderItem *item = FOLDER_ITEM(gnode->data);
1825 cm_return_val_if_fail(item != NULL, FALSE);
1827 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1828 folderview_update_node(folderview, cnode);
1833 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1836 FolderView *folderview = (FolderView *)data;
1839 if (GTK_CMCTREE_ROW(node)->children) {
1840 item = gtk_cmctree_node_get_row_data(ctree, node);
1841 cm_return_if_fail(item != NULL);
1843 if (!item->collapsed)
1844 gtk_cmctree_expand(ctree, node);
1846 folderview_update_node(folderview, node);
1850 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1851 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1854 GtkCMCTreeNode *node, *parent, *sibling;
1856 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1858 g_warning("%s not found.", item->path);
1860 parent = GTK_CMCTREE_ROW(node)->parent;
1861 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1862 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1864 sibling = GTK_CMCTREE_ROW(parent)->children;
1868 tmp = gtk_cmctree_node_get_row_data
1870 if (tmp && tmp->stype != F_NORMAL)
1871 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1875 if (node != sibling)
1876 gtk_cmctree_move(ctree, node, parent, sibling);
1883 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1886 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1887 GtkCMCTreeNode *prev = NULL;
1889 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1890 gtk_sctree_sort_recursive(ctree, root);
1891 if (root && GTK_CMCTREE_ROW(root)->parent) {
1892 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1895 set_special_folder(ctree, folder->inbox, root, &prev);
1896 set_special_folder(ctree, folder->outbox, root, &prev);
1897 set_special_folder(ctree, folder->draft, root, &prev);
1898 set_special_folder(ctree, folder->queue, root, &prev);
1899 set_special_folder(ctree, folder->trash, root, &prev);
1900 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1903 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1905 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1906 GtkCMCTreeNode *root;
1908 cm_return_if_fail(folder != NULL);
1910 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1911 folderview_gnode_func, folderview);
1912 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1914 folderview_sort_folders(folderview, root, folder);
1917 /* callback functions */
1918 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1919 GdkEventButton *event)
1923 FolderViewPopup *fpopup;
1924 GtkActionGroup *action_group;
1926 FolderItem *special_trash = NULL, *special_queue = NULL;
1928 GtkUIManager *ui_manager = gtk_ui_manager_new();
1930 if (folderview->ui_manager)
1931 g_object_unref(folderview->ui_manager);
1933 folderview->ui_manager = ui_manager;
1934 item = folderview_get_selected_item(folderview);
1936 cm_return_if_fail(item != NULL);
1937 cm_return_if_fail(item->folder != NULL);
1938 folder = item->folder;
1940 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1943 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1945 fpopup = g_hash_table_lookup(folderview_popups, "common");
1946 action_group = g_hash_table_lookup(folderview->popups, "common");
1949 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1950 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1953 if (fpopup->add_menuitems)
1954 fpopup->add_menuitems(ui_manager, item);
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1960 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1961 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1962 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1963 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1964 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1966 if (fpopup->set_sensitivity != NULL)
1967 fpopup->set_sensitivity(ui_manager, item);
1969 if (NULL != (ac = account_find_from_item(item))) {
1970 special_trash = account_get_special_folder(ac, F_TRASH);
1971 special_queue = account_get_special_folder(ac, F_QUEUE);
1974 if ((item == folder->trash || item == special_trash
1975 || folder_has_parent_of_type(item, F_TRASH))) {
1976 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1980 if ((item == folder->queue || item == special_queue
1981 || folder_has_parent_of_type(item, F_QUEUE))) {
1982 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1983 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1986 #define SET_SENS(name, sens) \
1987 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1989 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1990 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1991 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1992 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1993 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1994 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1995 folderview->selected == folderview->opened);
1996 SET_SENS("FolderViewPopup/Properties", TRUE);
1998 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1999 item->total_msgs >= 1 && !item->processing_pending);
2000 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
2001 !item->no_select && !item->processing_pending);
2003 if (item == folder->trash || item == special_trash
2004 || folder_has_parent_of_type(item, F_TRASH)) {
2005 GSList *msglist = folder_item_get_msg_list(item);
2006 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2007 procmsg_msg_list_free(msglist);
2009 if (item == folder->queue || item == special_queue
2010 || folder_has_parent_of_type(item, F_QUEUE)) {
2011 GSList *msglist = folder_item_get_msg_list(item);
2012 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2013 procmsg_msg_list_free(msglist);
2017 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2018 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2019 g_signal_connect(G_OBJECT(popup), "selection_done",
2020 G_CALLBACK(folderview_popup_close),
2022 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2023 event->button, event->time);
2026 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2027 FolderView *folderview)
2029 GtkCMCList *clist = GTK_CMCLIST(ctree);
2030 gint prev_row = -1, row = -1, column = -1;
2032 if (!event) return FALSE;
2034 if (event->button == 1 || event->button == 2) {
2035 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2036 folderview->open_folder = TRUE;
2038 if (event->type == GDK_2BUTTON_PRESS) {
2039 if (clist->selection) {
2040 GtkCMCTreeNode *node;
2042 node = GTK_CMCTREE_NODE(clist->selection->data);
2044 gtk_cmctree_toggle_expansion(
2047 folderview->open_folder = FALSE;
2054 if (event->button == 2 || event->button == 3) {
2056 if (clist->selection) {
2057 GtkCMCTreeNode *node;
2059 node = GTK_CMCTREE_NODE(clist->selection->data);
2061 prev_row = gtkut_ctree_get_nth_from_node
2062 (GTK_CMCTREE(ctree), node);
2065 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2068 if (prev_row != row) {
2069 gtk_cmclist_unselect_all(clist);
2070 if (event->button == 2)
2071 folderview_select_node
2073 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2076 gtk_cmclist_select_row(clist, row, column);
2080 if (event->button != 3) return FALSE;
2082 folderview_set_sens_and_popup_menu(folderview, row, event);
2086 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2087 FolderView *folderview)
2089 int row = -1, column = -1;
2091 if (!event) return FALSE;
2093 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2096 if (event->button == 1 && folderview->open_folder == FALSE &&
2097 folderview->opened != NULL) {
2098 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2099 folderview->opened);
2100 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2106 #define BREAK_ON_MODIFIER_KEY() \
2107 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2109 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2110 FolderView *folderview)
2112 GtkCMCTreeNode *node;
2115 if (!event) return FALSE;
2117 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2120 switch (event->keyval) {
2122 if (folderview->selected) {
2123 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2124 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2125 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2126 folderview->selected);
2128 folderview_select_node(folderview,
2129 folderview->selected);
2133 case GDK_KEY_Return:
2134 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2135 gtk_cmctree_toggle_expansion(
2136 GTK_CMCTREE(folderview->ctree),
2137 folderview->selected);
2141 case GDK_KEY_Return:
2142 case GDK_KEY_KP_Enter:
2143 if (folderview->selected)
2144 folderview_select_node(folderview, folderview->selected);
2148 BREAK_ON_MODIFIER_KEY();
2149 if (folderview->selected) {
2150 if (folderview->opened == folderview->selected &&
2151 (!folderview->summaryview->folder_item ||
2152 folderview->summaryview->folder_item->total_msgs == 0))
2153 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2155 folderview_select_node(folderview,
2156 folderview->selected);
2160 if (folderview->selected) {
2161 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2162 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2163 folderview->selected);
2165 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2166 folderview->selected))) {
2167 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2168 NULL, folder_item_parent(item)))) {
2169 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2170 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2171 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2180 if (event->keyval == GDK_KEY_Home)
2181 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2183 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2184 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2186 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2188 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2189 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2199 typedef struct _PostponedSelectData
2202 GtkCMCTreeNode *row;
2204 FolderView *folderview;
2205 } PostponedSelectData;
2207 static gboolean postpone_select(void *data)
2209 PostponedSelectData *psdata = (PostponedSelectData *)data;
2210 debug_print("trying again\n");
2212 psdata->folderview->postpone_select_id = 0;
2213 psdata->folderview->open_folder = TRUE;
2214 main_window_cursor_normal(psdata->folderview->mainwin);
2215 STATUSBAR_POP(psdata->folderview->mainwin);
2216 folderview_selected(psdata->ctree, psdata->row,
2217 psdata->column, psdata->folderview);
2222 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2224 if (folderview->opened) {
2226 folderview->opened = NULL;
2230 FolderItem *olditem =
2231 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2232 folderview->opened);
2234 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2235 olditem->path ? olditem->path:olditem->name);
2236 /* will be null if we just moved the previously opened folder */
2237 STATUSBAR_PUSH(folderview->mainwin, buf);
2238 main_window_cursor_wait(folderview->mainwin);
2240 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2241 summary_show(folderview->summaryview, NULL);
2242 folder_item_close(olditem);
2243 main_window_cursor_normal(folderview->mainwin);
2244 STATUSBAR_POP(folderview->mainwin);
2245 if (olditem->folder->klass->item_closed)
2246 olditem->folder->klass->item_closed(olditem);
2251 if (folderview->opened &&
2252 !GTK_CMCTREE_ROW(folderview->opened)->children)
2253 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2255 folderview->opened = NULL;
2257 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2258 gint column, FolderView *folderview)
2260 static gboolean can_select = TRUE; /* exclusive lock */
2265 GtkCMCTreeNode *old_opened = folderview->opened;
2267 folderview->selected = row;
2269 debug_print("newly selected %p, opened %p\n", folderview->selected,
2270 folderview->opened);
2271 if (folderview->opened == row) {
2272 folderview->open_folder = FALSE;
2277 item = gtk_cmctree_node_get_row_data(ctree, row);
2280 folderview->open_folder = FALSE;
2284 if (!can_select || summary_is_locked(folderview->summaryview)) {
2285 if (folderview->opened) {
2286 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2287 gtk_cmctree_select(ctree, folderview->opened);
2289 folderview->open_folder = FALSE;
2294 if (!folderview->open_folder) {
2301 /* Save cache for old folder */
2302 /* We don't want to lose all caches if sylpheed crashed */
2303 /* resets folderview->opened to NULL */
2304 folderview_close_opened(folderview, FALSE);
2306 /* CLAWS: set compose button type: news folder items
2307 * always have a news folder as parent */
2309 toolbar_set_compose_button
2310 (folderview->mainwin->toolbar,
2311 FOLDER_TYPE(item->folder) == F_NEWS ?
2312 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2315 debug_print("Folder %s is selected\n", item->path);
2317 if (!GTK_CMCTREE_ROW(row)->children)
2318 gtk_cmctree_expand(ctree, row);
2320 /* ungrab the mouse event */
2321 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2322 gtk_grab_remove(GTK_WIDGET(ctree));
2323 if (gdk_pointer_is_grabbed())
2324 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2328 /* TODO: wwp: avoid displaying (null) in the status bar */
2329 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2330 item->path : "(null)");
2331 debug_print("%s\n", buf);
2332 STATUSBAR_PUSH(folderview->mainwin, buf);
2335 main_window_cursor_wait(folderview->mainwin);
2337 if (folderview->scanning_folder == item->folder) {
2340 res = folder_item_open(item);
2343 if (res == -1 && item->no_select == FALSE) {
2344 main_window_cursor_normal(folderview->mainwin);
2345 STATUSBAR_POP(folderview->mainwin);
2347 alertpanel_error(_("Folder could not be opened."));
2349 folderview->open_folder = FALSE;
2353 } else if (res == -2 && item->no_select == FALSE) {
2354 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2355 data->ctree = ctree;
2357 data->column = column;
2358 data->folderview = folderview;
2359 debug_print("postponing open of %s till end of scan\n",
2360 item->path ? item->path:item->name);
2361 folderview->open_folder = FALSE;
2363 if (folderview->postpone_select_id != 0)
2364 g_source_remove(folderview->postpone_select_id);
2365 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2370 main_window_cursor_normal(folderview->mainwin);
2373 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2374 opened = summary_show(folderview->summaryview, item);
2376 folder_clean_cache_memory(item);
2379 gtkut_ctree_set_focus_row(ctree, old_opened);
2380 gtk_cmctree_select(ctree, old_opened);
2381 folderview->opened = old_opened;
2383 folderview->opened = row;
2384 if (gtk_cmctree_node_is_visible(ctree, row)
2385 != GTK_VISIBILITY_FULL)
2386 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2389 STATUSBAR_POP(folderview->mainwin);
2391 folderview->open_folder = FALSE;
2396 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2397 FolderView *folderview)
2401 item = gtk_cmctree_node_get_row_data(ctree, node);
2402 cm_return_if_fail(item != NULL);
2403 item->collapsed = FALSE;
2404 folderview_update_node(folderview, node);
2407 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2408 FolderView *folderview)
2412 item = gtk_cmctree_node_get_row_data(ctree, node);
2413 cm_return_if_fail(item != NULL);
2414 item->collapsed = TRUE;
2415 folderview_update_node(folderview, node);
2418 static void folderview_popup_close(GtkMenuShell *menu_shell,
2419 FolderView *folderview)
2421 if (!folderview->opened) return;
2423 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2426 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2427 FolderView *folderview)
2429 FolderColumnType type = folderview->col_state[column].type;
2431 prefs_common.folder_col_size[type] = width;
2434 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2436 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2437 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2438 GtkCMCTreeNode *node, *parent_node;
2439 gint *col_pos = folderview->col_pos;
2440 FolderItemUpdateData hookdata;
2442 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2443 if (parent_node == NULL)
2446 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2448 text[col_pos[F_COL_FOLDER]] = item->name;
2449 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2454 gtk_cmctree_expand(ctree, parent_node);
2455 gtk_cmctree_node_set_row_data(ctree, node, item);
2457 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2458 folderview_sort_folders(folderview, parent_node, item->folder);
2460 hookdata.item = item;
2461 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2462 hookdata.msg = NULL;
2463 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2465 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2468 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2470 FolderView *folderview = (FolderView *)data;
2472 GSList *mlist = NULL;
2474 FolderItem *special_trash = NULL;
2477 if (!folderview->selected) return;
2478 item = folderview_get_selected_item(folderview);
2479 cm_return_if_fail(item != NULL);
2480 cm_return_if_fail(item->folder != NULL);
2482 if (NULL != (ac = account_find_from_item(item)))
2483 special_trash = account_get_special_folder(ac, F_TRASH);
2485 if (item != item->folder->trash && item != special_trash
2486 && !folder_has_parent_of_type(item, F_TRASH)) return;
2488 if (prefs_common.ask_on_clean) {
2489 if (alertpanel(_("Empty trash"),
2490 _("Delete all messages in trash?"),
2491 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2495 mlist = folder_item_get_msg_list(item);
2497 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2498 MsgInfo * msginfo = (MsgInfo *) cur->data;
2499 if (MSG_IS_LOCKED(msginfo->flags))
2501 /* is it partially received? (partial_recv isn't cached) */
2502 if (msginfo->total_size != 0 &&
2503 msginfo->size != (off_t)msginfo->total_size)
2504 partial_mark_for_delete(msginfo);
2506 procmsg_msg_list_free(mlist);
2508 folder_item_remove_all_msg(item);
2511 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2513 FolderView *folderview = (FolderView *)data;
2515 FolderItem *special_queue = NULL;
2517 gchar *errstr = NULL;
2519 if (!folderview->selected) return;
2520 item = folderview_get_selected_item(folderview);
2521 cm_return_if_fail(item != NULL);
2522 cm_return_if_fail(item->folder != NULL);
2524 if (NULL != (ac = account_find_from_item(item)))
2525 special_queue = account_get_special_folder(ac, F_QUEUE);
2527 if (item != item->folder->queue && item != special_queue
2528 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2530 if (procmsg_queue_is_empty(item))
2533 if (prefs_common.work_offline)
2534 if (alertpanel(_("Offline warning"),
2535 _("You're working offline. Override?"),
2536 GTK_STOCK_NO, GTK_STOCK_YES,
2537 NULL) != G_ALERTALTERNATE)
2540 /* ask for confirmation before sending queued messages only
2541 in online mode and if there is at least one message queued
2542 in any of the folder queue
2544 if (prefs_common.confirm_send_queued_messages) {
2545 if (!prefs_common.work_offline) {
2546 if (alertpanel(_("Send queued messages"),
2547 _("Send all queued messages?"),
2548 GTK_STOCK_CANCEL, _("_Send"),
2549 NULL) != G_ALERTALTERNATE)
2554 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2556 alertpanel_error_log(_("Some errors occurred while "
2557 "sending queued messages."));
2559 alertpanel_error_log(_("Some errors occurred "
2560 "while sending queued messages:\n%s"), errstr);
2566 static void folderview_search_cb(GtkAction *action, gpointer data)
2568 FolderView *folderview = (FolderView *)data;
2569 summary_search(folderview->summaryview);
2572 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2574 FolderView *folderview = (FolderView *)data;
2577 if (!folderview->selected) return;
2579 item = folderview_get_selected_item(folderview);
2580 cm_return_if_fail(item != NULL);
2581 cm_return_if_fail(item->folder != NULL);
2583 item->processing_pending = TRUE;
2584 folder_item_apply_processing(item);
2585 item->processing_pending = FALSE;
2588 static void folderview_property_cb(GtkAction *action, gpointer data)
2590 FolderView *folderview = (FolderView *)data;
2593 if (!folderview->selected) return;
2595 item = folderview_get_selected_item(folderview);
2596 cm_return_if_fail(item != NULL);
2597 cm_return_if_fail(item->folder != NULL);
2599 prefs_folder_item_open(item);
2602 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2604 GSList *list = NULL;
2605 GSList *done = NULL;
2606 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2608 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2609 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2610 && list->data != node) {
2611 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2612 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2615 for (list = done; list != NULL; list = g_slist_next(list)) {
2616 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2622 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2623 FolderItem *to_folder, gboolean copy)
2625 FolderItem *new_folder = NULL;
2629 cm_return_if_fail(folderview != NULL);
2630 cm_return_if_fail(from_folder != NULL);
2631 cm_return_if_fail(to_folder != NULL);
2633 if (prefs_common.warn_dnd) {
2634 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2635 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2636 from_folder->name, to_folder->name);
2637 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2638 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2639 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2642 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2644 else if (status & G_ALERTDISABLE)
2645 prefs_common.warn_dnd = FALSE;
2648 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2649 from_folder->name, to_folder->name);
2650 STATUSBAR_PUSH(folderview->mainwin, buf);
2652 summary_clear_all(folderview->summaryview);
2653 folderview->opened = NULL;
2654 folderview->selected = NULL;
2655 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2657 main_window_cursor_wait(folderview->mainwin);
2659 statusbar_verbosity_set(FALSE);
2660 folder_item_update_freeze();
2661 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2662 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2663 statusbar_verbosity_set(FALSE);
2664 main_window_cursor_normal(folderview->mainwin);
2665 STATUSBAR_POP(folderview->mainwin);
2666 folder_item_update_thaw();
2667 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2669 folderview_sort_folders(folderview,
2670 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2671 NULL, to_folder), new_folder->folder);
2672 folderview_select(folderview, new_folder);
2673 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2675 statusbar_verbosity_set(FALSE);
2676 main_window_cursor_normal(folderview->mainwin);
2677 STATUSBAR_POP(folderview->mainwin);
2678 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2679 folder_item_update_thaw();
2681 case F_MOVE_FAILED_DEST_IS_PARENT:
2682 alertpanel_error(_("Source and destination are the same."));
2684 case F_MOVE_FAILED_DEST_IS_CHILD:
2685 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2686 _("Can't move a folder to one of its children."));
2688 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2689 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2692 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2697 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2700 static gint folderview_clist_compare(GtkCMCList *clist,
2701 gconstpointer ptr1, gconstpointer ptr2)
2703 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2704 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2706 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2708 return item1->order - item2->order;
2711 // if only one folder has an order it comes first
2712 if (item1->order > 0)
2716 if (item2->order > 0)
2722 return (item2->name != NULL);
2726 return g_utf8_collate(item1->name, item2->name);
2729 static void folderview_processing_cb(GtkAction *action, gpointer data)
2731 FolderView *folderview = (FolderView *)data;
2735 if (!folderview->selected) return;
2737 item = folderview_get_selected_item(folderview);
2738 cm_return_if_fail(item != NULL);
2739 cm_return_if_fail(item->folder != NULL);
2741 id = folder_item_get_identifier(item);
2742 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2745 prefs_filtering_open(&item->prefs->processing, title,
2746 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2750 void folderview_set_target_folder_color(gint color_op)
2754 FolderView *folderview;
2756 for (list = folderview_list; list != NULL; list = list->next) {
2757 folderview = (FolderView *)list->data;
2758 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2760 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2761 folderview->color_op;
2767 static gchar *last_smallfont = NULL;
2768 static gchar *last_normalfont = NULL;
2769 static gchar *last_boldfont = NULL;
2770 static gboolean last_derive = 0;
2772 void folderview_reinit_fonts(FolderView *folderview)
2775 g_free(last_smallfont);
2776 last_smallfont = NULL;
2777 g_free(last_normalfont);
2778 last_normalfont = NULL;
2779 g_free(last_boldfont);
2780 last_boldfont = NULL;
2783 void folderview_reflect_prefs(void)
2785 gboolean update_font = FALSE;
2786 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2787 FolderItem *item = folderview_get_selected_item(folderview);
2788 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2789 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2790 gint height = gtk_adjustment_get_value(pos);
2792 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2793 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2794 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2795 last_derive != prefs_common.derive_from_normal_font)
2801 g_free(last_smallfont);
2802 last_smallfont = g_strdup(SMALL_FONT);
2803 g_free(last_normalfont);
2804 last_normalfont = g_strdup(NORMAL_FONT);
2805 g_free(last_boldfont);
2806 last_boldfont = g_strdup(BOLD_FONT);
2807 last_derive = prefs_common.derive_from_normal_font;
2809 #define STYLE_FREE(s) \
2811 g_object_unref(s); \
2815 STYLE_FREE(normal_style);
2816 STYLE_FREE(normal_color_style);
2817 STYLE_FREE(bold_style);
2818 STYLE_FREE(bold_color_style);
2819 STYLE_FREE(bold_tgtfold_style);
2823 folderview_init(folderview);
2824 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2825 folderview_column_set_titles(folderview);
2826 folderview_set_all();
2828 g_signal_handlers_block_by_func
2829 (G_OBJECT(folderview->ctree),
2830 G_CALLBACK(folderview_selected), folderview);
2833 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2834 GTK_CMCTREE(folderview->ctree), NULL, item);
2836 folderview_select(folderview, item);
2837 folderview->open_folder = FALSE;
2838 folderview->selected = node;
2841 g_signal_handlers_unblock_by_func
2842 (G_OBJECT(folderview->ctree),
2843 G_CALLBACK(folderview_selected), folderview);
2845 pos = gtk_scrolled_window_get_vadjustment(
2846 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2847 gtk_adjustment_set_value(pos, height);
2848 gtk_adjustment_changed(pos);
2849 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2852 static void drag_state_stop(FolderView *folderview)
2854 if (folderview->drag_timer_id)
2855 g_source_remove(folderview->drag_timer_id);
2856 folderview->drag_timer_id = 0;
2857 folderview->drag_node = NULL;
2860 static gboolean folderview_defer_expand(FolderView *folderview)
2862 if (folderview->drag_node) {
2863 folderview_recollapse_nodes(folderview, folderview->drag_node);
2864 if (folderview->drag_item->collapsed) {
2865 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2866 folderview->nodes_to_recollapse = g_slist_append
2867 (folderview->nodes_to_recollapse, folderview->drag_node);
2870 folderview->drag_item = NULL;
2871 folderview->drag_timer_id = 0;
2875 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2877 /* the idea is that we call drag_state_start() whenever we want expansion to
2878 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2879 * we need to call drag_state_stop() */
2880 drag_state_stop(folderview);
2881 /* request expansion */
2882 if (0 != (folderview->drag_timer_id = g_timeout_add
2883 (prefs_common.hover_timeout,
2884 (GSourceFunc)folderview_defer_expand,
2886 folderview->drag_node = node;
2887 folderview->drag_item = item;
2890 #ifndef GENERIC_UMPC
2891 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2892 FolderView *folderview)
2894 GdkDragContext *context;
2896 cm_return_if_fail(folderview != NULL);
2897 if (folderview->selected == NULL) return;
2898 if (folderview->nodes_to_recollapse)
2899 g_slist_free(folderview->nodes_to_recollapse);
2900 folderview->nodes_to_recollapse = NULL;
2901 context = gtk_drag_begin(widget, folderview->target_list,
2902 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2903 gtk_drag_set_icon_default(context);
2906 static void folderview_drag_data_get(GtkWidget *widget,
2907 GdkDragContext *drag_context,
2908 GtkSelectionData *selection_data,
2911 FolderView *folderview)
2915 gchar *source = NULL;
2916 if (info == TARGET_DUMMY) {
2917 sel = GTK_CMCLIST(folderview->ctree)->selection;
2921 item = gtk_cmctree_node_get_row_data
2922 (GTK_CMCTREE(folderview->ctree),
2923 GTK_CMCTREE_NODE(sel->data));
2925 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2926 gtk_selection_data_set(selection_data,
2927 gtk_selection_data_get_target(selection_data), 8,
2928 source, strlen(source));
2931 g_warning("unknown info %d", info);
2935 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2937 FolderUpdateData *hookdata;
2938 FolderView *folderview;
2942 folderview = (FolderView *) userdata;
2943 cm_return_val_if_fail(hookdata != NULL, FALSE);
2944 cm_return_val_if_fail(folderview != NULL, FALSE);
2946 ctree = folderview->ctree;
2947 cm_return_val_if_fail(ctree != NULL, FALSE);
2949 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2950 folderview_create_folder_node(folderview, hookdata->item);
2951 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2952 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2953 NULL, folder_item_parent(hookdata->item));
2954 folderview_sort_folders(folderview, node, hookdata->folder);
2955 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2956 GtkCMCTreeNode *node;
2958 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2960 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2961 if (folderview->selected == node)
2962 folderview->selected = NULL;
2963 if (folderview->opened == node)
2964 folderview->opened = NULL;
2966 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2967 /* do nothing, it's done by the ADD and REMOVE) */
2968 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2969 folderview_set(folderview);
2974 static gboolean folderview_dnd_scroll_cb(gpointer data)
2976 FolderView *folderview = (FolderView *)data;
2977 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2978 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2979 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2980 gint max = (int)gtk_adjustment_get_upper(pos) -
2981 (int)gtk_adjustment_get_page_size(pos);
2983 if (folderview->scroll_value == 0) {
2984 folderview->scroll_timeout_id = 0;
2988 if (folderview->scroll_value > 0 && new_val > max) {
2990 } else if (folderview->scroll_value < 0 && new_val < 0) {
2993 gtk_adjustment_set_value(pos, new_val);
2998 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2999 GdkDragContext *context,
3003 FolderView *folderview)
3006 FolderItem *item = NULL, *src_item = NULL;
3007 GtkCMCTreeNode *node = NULL;
3008 gboolean acceptable = FALSE;
3009 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3010 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3011 int height = (int)gtk_adjustment_get_page_size(pos);
3012 int total_height = (int)gtk_adjustment_get_upper(pos);
3013 int vpos = (int)gtk_adjustment_get_value(pos);
3014 int offset = prefs_common.show_col_headers ? 24:0;
3017 if (gtk_cmclist_get_selection_info
3018 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3019 GtkWidget *srcwidget;
3021 if (y > height - (48 - offset) && height + vpos < total_height) {
3022 dist = -(height - (48 - offset) - y);
3023 folderview->scroll_value = 1.41f * (1+(dist / 6));
3024 } else if (y < 72 - (24 - offset) && y >= 0) {
3025 dist = 72 - (24 - offset) - y;
3026 folderview->scroll_value = -1.41f * (1+(dist / 6));
3028 folderview->scroll_value = 0;
3030 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3031 folderview->scroll_timeout_id =
3032 g_timeout_add(30, folderview_dnd_scroll_cb,
3036 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3037 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3038 src_item = folderview->summaryview->folder_item;
3040 srcwidget = gtk_drag_get_source_widget(context);
3041 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3042 /* comes from summaryview */
3043 /* we are copying messages, so only accept folder items that are not
3044 the source item, are no root items and can copy messages */
3045 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3046 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3047 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3049 } else if (srcwidget == folderview->ctree) {
3050 /* comes from folderview */
3051 /* we are moving folder items, only accept folders that are not
3052 the source items and can copy messages and create folder items */
3053 if (item && item->folder && src_item && src_item != item &&
3054 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3055 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3056 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3057 || item->folder == src_item->folder))
3060 /* comes from another app */
3061 /* we are adding messages, so only accept folder items that are
3062 no root items and can copy messages */
3063 if (item && item->folder && folder_item_parent(item) != NULL
3064 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3065 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3070 if (acceptable || (src_item && src_item == item))
3071 drag_state_start(folderview, node, item);
3074 g_signal_handlers_block_by_func
3076 G_CALLBACK(folderview_selected), folderview);
3077 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3078 g_signal_handlers_unblock_by_func
3080 G_CALLBACK(folderview_selected), folderview);
3081 gdk_drag_status(context,
3082 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3083 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3085 if (folderview->opened)
3086 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3087 gdk_drag_status(context, 0, time);
3093 static void folderview_drag_leave_cb(GtkWidget *widget,
3094 GdkDragContext *context,
3096 FolderView *folderview)
3098 drag_state_stop(folderview);
3099 folderview->scroll_value = 0;
3100 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3103 static void free_info (gpointer stuff, gpointer data)
3108 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3109 guint time, FolderItem *item)
3112 GSList *msglist = NULL;
3113 list = uri_list_extract_filenames(data);
3114 if (!(item && item->folder && folder_item_parent(item) != NULL
3115 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3117 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3118 debug_print("item doesn't fit\n");
3122 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3123 debug_print("list is empty\n");
3126 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3127 MsgFileInfo *info = NULL;
3129 if (file_is_email((gchar *)tmp->data)) {
3130 info = g_new0(MsgFileInfo, 1);
3131 info->msginfo = NULL;
3132 info->file = (gchar *)tmp->data;
3133 msglist = g_slist_prepend(msglist, info);
3134 debug_print("file is a mail\n");
3136 debug_print("file isn't a mail\n");
3140 msglist = g_slist_reverse(msglist);
3141 folder_item_add_msgs(item, msglist, FALSE);
3142 g_slist_foreach(msglist, free_info, NULL);
3143 g_slist_free(msglist);
3144 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3146 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3148 list_free_strings(list);
3152 static void folderview_drag_received_cb(GtkWidget *widget,
3153 GdkDragContext *drag_context,
3156 GtkSelectionData *data,
3159 FolderView *folderview)
3162 FolderItem *item = NULL, *src_item;
3163 GtkCMCTreeNode *node;
3164 int offset = prefs_common.show_col_headers ? 24:0;
3166 folderview->scroll_value = 0;
3168 if (info == TARGET_DUMMY) {
3169 drag_state_stop(folderview);
3170 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3171 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3172 /* comes from summaryview */
3173 if (gtk_cmclist_get_selection_info
3174 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3177 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3178 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3179 src_item = folderview->summaryview->folder_item;
3181 if (item->no_select) {
3182 alertpanel_error(_("The destination folder can only be used to "
3183 "store subfolders."));
3186 /* re-check (due to acceptable possibly set for folder moves */
3187 if (!(item && item->folder && item->path && !item->no_select &&
3188 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3191 if (item && src_item) {
3192 switch (gdk_drag_context_get_selected_action(drag_context)) {
3193 case GDK_ACTION_COPY:
3194 summary_copy_selected_to(folderview->summaryview, item);
3195 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3197 case GDK_ACTION_MOVE:
3198 case GDK_ACTION_DEFAULT:
3200 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3201 summary_copy_selected_to(folderview->summaryview, item);
3203 summary_move_selected_to(folderview->summaryview, item);
3204 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3207 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3209 /* comes from folderview */
3211 gboolean folder_is_normal = TRUE;
3212 gboolean copy = (GDK_ACTION_COPY ==
3213 gdk_drag_context_get_selected_action(drag_context));
3215 source = (char *)gtk_selection_data_get_data(data) + 17;
3216 if (gtk_cmclist_get_selection_info
3217 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3219 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3222 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3223 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3224 src_item = folder_find_item_from_identifier(source);
3228 src_item->stype == F_NORMAL &&
3229 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3230 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3231 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3232 !folder_has_parent_of_type(src_item, F_TRASH);
3233 if (!item || !src_item || !folder_is_normal) {
3234 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3238 folderview_move_folder(folderview, src_item, item, copy);
3239 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3241 folderview->nodes_to_recollapse = NULL;
3242 } else if (info == TARGET_MAIL_URI_LIST) {
3243 if (gtk_cmclist_get_selection_info
3244 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3247 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3249 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3250 debug_print("no node\n");
3253 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3255 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3256 debug_print("no item\n");
3259 folderview_finish_dnd(gtk_selection_data_get_data(data),
3260 drag_context, time, item);
3264 static void folderview_drag_end_cb(GtkWidget *widget,
3265 GdkDragContext *drag_context,
3266 FolderView *folderview)
3268 drag_state_stop(folderview);
3269 folderview->scroll_value = 0;
3270 g_slist_free(folderview->nodes_to_recollapse);
3271 folderview->nodes_to_recollapse = NULL;
3274 void folderview_register_popup(FolderViewPopup *fpopup)
3278 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3279 FolderView *folderview = folderviews->data;
3280 GtkActionGroup *factory;
3282 factory = create_action_group(folderview, fpopup);
3283 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3285 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3288 void folderview_unregister_popup(FolderViewPopup *fpopup)
3293 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3294 FolderView *folderview = folderviews->data;
3296 g_hash_table_remove(folderview->popups, fpopup->klass);
3298 g_hash_table_remove(folderview_popups, fpopup->klass);
3301 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3303 g_return_if_fail(folderview != NULL);
3304 g_return_if_fail(item != NULL);
3306 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3307 g_return_if_fail(ctree != NULL);
3309 GtkCMCTreeNode *node =
3310 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3311 g_return_if_fail(node != NULL);
3313 gtk_cmctree_remove_node(ctree, node);
3316 void folderview_freeze(FolderView *folderview)
3319 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3322 void folderview_thaw(FolderView *folderview)
3325 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3328 void folderview_grab_focus(FolderView *folderview)
3331 gtk_widget_grab_focus(folderview->ctree);