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", NULL, NULL , NULL},
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, "---", NULL, NULL , 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,
1428 static gboolean folderview_have_read_children(FolderView *folderview,
1431 return folderview_have_read_children_sub(folderview, item, FALSE);
1434 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1440 if (!item || !item->folder || !item->folder->node)
1443 node = item->folder->node;
1445 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1446 node = node->children;
1448 if (in_sub && item->search_match){
1452 while (node != NULL) {
1453 if (node && node->data) {
1454 FolderItem *next_item = (FolderItem*) node->data;
1456 if (folderview_have_matching_children_sub(folderview,
1466 static gboolean folderview_have_matching_children(FolderView *folderview,
1469 return folderview_have_matching_children_sub(folderview, item, FALSE);
1472 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1478 if (!item || !item->folder || !item->folder->node)
1481 node = item->folder->node;
1483 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1484 node = node->children;
1486 if (item->marked_msgs != 0) {
1490 while (node != NULL) {
1491 if (node && node->data) {
1492 FolderItem *next_item = (FolderItem*) node->data;
1494 if (folderview_have_marked_children_sub(folderview,
1503 static gboolean folderview_have_marked_children(FolderView *folderview,
1506 return folderview_have_marked_children_sub(folderview, item, FALSE);
1509 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1511 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1512 GtkStyle *style = NULL;
1513 GtkStyle *color_style = NULL;
1515 GdkPixbuf *xpm, *openxpm;
1516 static GdkPixbuf *searchicon;
1517 gboolean mark = FALSE;
1520 gboolean add_unread_mark;
1521 gboolean add_sub_match_mark;
1522 gboolean use_bold, use_color;
1523 gint *col_pos = folderview->col_pos;
1524 SpecialFolderItemType stype;
1526 item = gtk_cmctree_node_get_row_data(ctree, node);
1527 cm_return_if_fail(item != NULL);
1529 if (!GTK_CMCTREE_ROW(node)->expanded)
1530 mark = folderview_have_marked_children(folderview, item);
1532 mark = (item->marked_msgs != 0);
1534 stype = item->stype;
1535 if (stype == F_NORMAL) {
1536 if (folder_has_parent_of_type(item, F_TRASH))
1538 else if (folder_has_parent_of_type(item, F_DRAFT))
1540 else if (folder_has_parent_of_type(item, F_OUTBOX))
1542 else if (folder_has_parent_of_type(item, F_QUEUE))
1547 if (item->hide_read_msgs || item->hide_read_threads) {
1548 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1549 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1551 xpm = mark?m_inboxxpm:inboxxpm;
1552 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1556 if (item->hide_read_msgs || item->hide_read_threads) {
1557 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1558 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1560 xpm = mark?m_outboxxpm:outboxxpm;
1561 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1565 if (item->hide_read_msgs || item->hide_read_threads) {
1566 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1567 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1569 xpm = mark?m_queuexpm:queuexpm;
1570 openxpm = mark?m_queueopenxpm:queueopenxpm;
1574 if (item->hide_read_msgs || item->hide_read_threads) {
1575 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1576 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1578 xpm = mark?m_trashxpm:trashxpm;
1579 openxpm = mark?m_trashopenxpm:trashopenxpm;
1583 xpm = mark?m_draftsxpm:draftsxpm;
1584 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1588 FOLDER_TYPE(item->folder) == F_IMAP &&
1589 item->folder->account->imap_subsonly) {
1590 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1591 openxpm = foldersubsopenxpm;
1592 } else if (item->no_select) {
1593 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1594 openxpm = foldernoselectopenxpm;
1595 } else if (item->hide_read_msgs || item->hide_read_threads) {
1596 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1597 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1599 xpm = mark?m_folderxpm:folderxpm;
1600 openxpm = mark?m_folderopenxpm:folderopenxpm;
1604 name = folder_item_get_name(item);
1606 if (!GTK_CMCTREE_ROW(node)->expanded) {
1607 add_unread_mark = folderview_have_unread_children(
1609 add_sub_match_mark = folderview_have_matching_children(
1612 add_unread_mark = FALSE;
1613 add_sub_match_mark = FALSE;
1616 if (item->search_match) {
1618 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1621 xpm = openxpm = searchicon;
1625 if (prefs_common.display_folder_unread) {
1626 if (folder_has_parent_of_type(item, F_QUEUE)) {
1627 /* only total_msgs matters here */
1628 if (item->total_msgs > 0) {
1629 /* show total number (should be equal to the unread number)
1631 str = g_strdup_printf("%s (%d%s%s)",
1632 name, item->total_msgs,
1633 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1634 (item->unreadmarked_msgs > 0) ? "!" : "");
1637 if (prefs_common.display_folder_unread == 1) {
1638 if (item->unread_msgs > 0) {
1639 /* show unread number and signs */
1640 str = g_strdup_printf("%s (%d%s%s)",
1641 name, item->unread_msgs,
1642 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1643 (item->unreadmarked_msgs > 0) ? "!" : "");
1646 if (item->total_msgs > 0) {
1647 /* show unread number, total number and signs if any */
1648 str = g_strdup_printf("%s (%d/%d%s%s)",
1649 name, item->unread_msgs, item->total_msgs,
1650 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1651 (item->unreadmarked_msgs > 0) ? "!" : "");
1655 if ((str == NULL) &&
1656 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1657 /* no unread/total numbers, but at least one sign */
1658 str = g_strdup_printf("%s (%s%s)",
1660 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1661 (item->unreadmarked_msgs > 0) ? "!" : "");
1665 /* last fallback, folder name only or with +! sign */
1666 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1667 str = g_strdup_printf("%s%s",
1669 } else if (item->unreadmarked_msgs > 0) {
1670 str = g_strdup_printf("%s%s",
1672 } else if (add_sub_match_mark) {
1673 str = g_strdup_printf("%s%s",
1676 str = g_strdup_printf("%s", name);
1679 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1681 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1685 if (!folder_item_parent(item)) {
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1687 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1688 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1691 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1692 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1695 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1696 folder_has_parent_of_type(item, F_TRASH)) {
1697 use_bold = use_color = FALSE;
1698 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1699 GSList *list = folder_item_get_msg_list(item);
1701 use_bold = use_color = FALSE;
1702 for (cur = list; cur; cur = cur->next) {
1703 MsgInfo *msginfo = (MsgInfo *)cur->data;
1704 if (!MSG_IS_DELETED(msginfo->flags)) {
1705 /* highlight queue folder if there are any messages */
1706 use_bold = use_color = TRUE;
1710 if (!GTK_CMCTREE_ROW(node)->expanded &&
1711 use_bold == FALSE &&
1712 folderview_have_children(folderview, item))
1713 use_bold = use_color = TRUE;
1714 procmsg_msg_list_free(list);
1716 /* if unread messages exist, print with bold font */
1717 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1719 /* if new messages exist, print with colored letter */
1721 (item->new_msgs > 0) ||
1723 folderview_have_new_children(folderview, item));
1726 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1731 if (item->prefs->color > 0 && !use_color) {
1732 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1733 color_style = gtk_style_copy(bold_style);
1734 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1735 style = color_style;
1736 } else if (use_color) {
1737 style = bold_color_style;
1740 if (item->op_count > 0) {
1741 style = bold_tgtfold_style;
1743 } else if (use_color) {
1744 style = normal_color_style;
1745 gtk_cmctree_node_set_foreground(ctree, node,
1746 &folderview->color_new);
1747 } else if (item->op_count > 0) {
1748 style = bold_tgtfold_style;
1749 } else if (item->prefs->color > 0) {
1751 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1752 color_style = gtk_style_copy(normal_style);
1753 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1754 style = color_style;
1756 style = normal_style;
1759 gtk_cmctree_node_set_row_style(ctree, node, style);
1761 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1762 folderview_update_node(folderview, node);
1765 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1768 FolderView *folderview;
1770 GtkCMCTreeNode *node;
1772 cm_return_if_fail(item != NULL);
1774 for (list = folderview_list; list != NULL; list = list->next) {
1775 folderview = (FolderView *)list->data;
1776 ctree = GTK_CMCTREE(folderview->ctree);
1778 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1779 if (node && item->search_match != matches) {
1780 item->search_match = matches;
1781 folderview_update_node(folderview, node);
1786 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1788 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1789 FolderView *folderview = (FolderView *)data;
1791 GtkCMCTreeNode *node;
1792 cm_return_val_if_fail(update_info != NULL, TRUE);
1793 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1794 cm_return_val_if_fail(folderview != NULL, FALSE);
1796 ctree = GTK_CMCTREE(folderview->ctree);
1798 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1801 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1802 folderview_update_node(folderview, node);
1804 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1805 update_info->item == folderview->summaryview->folder_item &&
1806 update_info->item != NULL)
1807 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1808 summary_show(folderview->summaryview, update_info->item);
1814 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1815 GNode *gnode, GtkCMCTreeNode *cnode,
1818 FolderView *folderview = (FolderView *)data;
1819 FolderItem *item = FOLDER_ITEM(gnode->data);
1821 cm_return_val_if_fail(item != NULL, FALSE);
1823 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1824 folderview_update_node(folderview, cnode);
1829 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1832 FolderView *folderview = (FolderView *)data;
1835 if (GTK_CMCTREE_ROW(node)->children) {
1836 item = gtk_cmctree_node_get_row_data(ctree, node);
1837 cm_return_if_fail(item != NULL);
1839 if (!item->collapsed)
1840 gtk_cmctree_expand(ctree, node);
1842 folderview_update_node(folderview, node);
1846 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1847 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1850 GtkCMCTreeNode *node, *parent, *sibling;
1852 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1854 g_warning("%s not found.", item->path);
1856 parent = GTK_CMCTREE_ROW(node)->parent;
1857 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1858 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1860 sibling = GTK_CMCTREE_ROW(parent)->children;
1864 tmp = gtk_cmctree_node_get_row_data
1866 if (tmp && tmp->stype != F_NORMAL)
1867 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1871 if (node != sibling)
1872 gtk_cmctree_move(ctree, node, parent, sibling);
1879 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1882 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1883 GtkCMCTreeNode *prev = NULL;
1885 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1886 gtk_sctree_sort_recursive(ctree, root);
1887 if (root && GTK_CMCTREE_ROW(root)->parent) {
1888 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1891 set_special_folder(ctree, folder->inbox, root, &prev);
1892 set_special_folder(ctree, folder->outbox, root, &prev);
1893 set_special_folder(ctree, folder->draft, root, &prev);
1894 set_special_folder(ctree, folder->queue, root, &prev);
1895 set_special_folder(ctree, folder->trash, root, &prev);
1896 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1899 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1901 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1902 GtkCMCTreeNode *root;
1904 cm_return_if_fail(folder != NULL);
1906 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1907 folderview_gnode_func, folderview);
1908 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1910 folderview_sort_folders(folderview, root, folder);
1913 /* callback functions */
1914 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1915 GdkEventButton *event)
1919 FolderViewPopup *fpopup;
1920 GtkActionGroup *action_group;
1922 FolderItem *special_trash = NULL, *special_queue = NULL;
1924 GtkUIManager *ui_manager = gtk_ui_manager_new();
1926 if (folderview->ui_manager)
1927 g_object_unref(folderview->ui_manager);
1929 folderview->ui_manager = ui_manager;
1930 item = folderview_get_selected_item(folderview);
1932 cm_return_if_fail(item != NULL);
1933 cm_return_if_fail(item->folder != NULL);
1934 folder = item->folder;
1936 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1939 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1941 fpopup = g_hash_table_lookup(folderview_popups, "common");
1942 action_group = g_hash_table_lookup(folderview->popups, "common");
1945 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1946 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1947 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1949 if (fpopup->add_menuitems)
1950 fpopup->add_menuitems(ui_manager, item);
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1960 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1962 if (fpopup->set_sensitivity != NULL)
1963 fpopup->set_sensitivity(ui_manager, item);
1965 if (NULL != (ac = account_find_from_item(item))) {
1966 special_trash = account_get_special_folder(ac, F_TRASH);
1967 special_queue = account_get_special_folder(ac, F_QUEUE);
1970 if ((item == folder->trash || item == special_trash
1971 || folder_has_parent_of_type(item, F_TRASH))) {
1972 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1973 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1976 if ((item == folder->queue || item == special_queue
1977 || folder_has_parent_of_type(item, F_QUEUE))) {
1978 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1979 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1982 #define SET_SENS(name, sens) \
1983 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1985 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1986 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1987 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1988 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1989 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1990 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1991 folderview->selected == folderview->opened);
1992 SET_SENS("FolderViewPopup/Properties", TRUE);
1994 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1995 item->total_msgs >= 1 && !item->processing_pending);
1996 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1997 !item->no_select && !item->processing_pending);
1999 if (item == folder->trash || item == special_trash
2000 || folder_has_parent_of_type(item, F_TRASH)) {
2001 GSList *msglist = folder_item_get_msg_list(item);
2002 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2003 procmsg_msg_list_free(msglist);
2005 if (item == folder->queue || item == special_queue
2006 || folder_has_parent_of_type(item, F_QUEUE)) {
2007 GSList *msglist = folder_item_get_msg_list(item);
2008 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2009 procmsg_msg_list_free(msglist);
2013 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2014 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2015 g_signal_connect(G_OBJECT(popup), "selection_done",
2016 G_CALLBACK(folderview_popup_close),
2018 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2019 event->button, event->time);
2022 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2023 FolderView *folderview)
2025 GtkCMCList *clist = GTK_CMCLIST(ctree);
2026 gint prev_row = -1, row = -1, column = -1;
2028 if (!event) return FALSE;
2030 if (event->button == 1 || event->button == 2) {
2031 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2032 folderview->open_folder = TRUE;
2034 if (event->type == GDK_2BUTTON_PRESS) {
2035 if (clist->selection) {
2036 GtkCMCTreeNode *node;
2038 node = GTK_CMCTREE_NODE(clist->selection->data);
2040 gtk_cmctree_toggle_expansion(
2043 folderview->open_folder = FALSE;
2050 if (event->button == 2 || event->button == 3) {
2052 if (clist->selection) {
2053 GtkCMCTreeNode *node;
2055 node = GTK_CMCTREE_NODE(clist->selection->data);
2057 prev_row = gtkut_ctree_get_nth_from_node
2058 (GTK_CMCTREE(ctree), node);
2061 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2064 if (prev_row != row) {
2065 gtk_cmclist_unselect_all(clist);
2066 if (event->button == 2)
2067 folderview_select_node
2069 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2072 gtk_cmclist_select_row(clist, row, column);
2076 if (event->button != 3) return FALSE;
2078 folderview_set_sens_and_popup_menu(folderview, row, event);
2082 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2083 FolderView *folderview)
2085 int row = -1, column = -1;
2087 if (!event) return FALSE;
2089 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2092 if (event->button == 1 && folderview->open_folder == FALSE &&
2093 folderview->opened != NULL) {
2094 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2095 folderview->opened);
2096 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2102 #define BREAK_ON_MODIFIER_KEY() \
2103 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2105 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2106 FolderView *folderview)
2108 GtkCMCTreeNode *node;
2111 if (!event) return FALSE;
2113 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2116 switch (event->keyval) {
2118 if (folderview->selected) {
2119 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2120 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2121 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2122 folderview->selected);
2124 folderview_select_node(folderview,
2125 folderview->selected);
2129 case GDK_KEY_Return:
2130 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2131 gtk_cmctree_toggle_expansion(
2132 GTK_CMCTREE(folderview->ctree),
2133 folderview->selected);
2137 case GDK_KEY_Return:
2138 case GDK_KEY_KP_Enter:
2139 if (folderview->selected)
2140 folderview_select_node(folderview, folderview->selected);
2144 BREAK_ON_MODIFIER_KEY();
2145 if (folderview->selected) {
2146 if (folderview->opened == folderview->selected &&
2147 (!folderview->summaryview->folder_item ||
2148 folderview->summaryview->folder_item->total_msgs == 0))
2149 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2151 folderview_select_node(folderview,
2152 folderview->selected);
2156 if (folderview->selected) {
2157 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2158 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2159 folderview->selected);
2161 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2162 folderview->selected))) {
2163 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2164 NULL, folder_item_parent(item)))) {
2165 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2166 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2167 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2176 if (event->keyval == GDK_KEY_Home)
2177 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2179 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2180 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2182 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2184 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2185 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2195 typedef struct _PostponedSelectData
2198 GtkCMCTreeNode *row;
2200 FolderView *folderview;
2201 } PostponedSelectData;
2203 static gboolean postpone_select(void *data)
2205 PostponedSelectData *psdata = (PostponedSelectData *)data;
2206 debug_print("trying again\n");
2208 psdata->folderview->postpone_select_id = 0;
2209 psdata->folderview->open_folder = TRUE;
2210 main_window_cursor_normal(psdata->folderview->mainwin);
2211 STATUSBAR_POP(psdata->folderview->mainwin);
2212 folderview_selected(psdata->ctree, psdata->row,
2213 psdata->column, psdata->folderview);
2218 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2220 if (folderview->opened) {
2222 folderview->opened = NULL;
2226 FolderItem *olditem =
2227 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2228 folderview->opened);
2230 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2231 olditem->path ? olditem->path:olditem->name);
2232 /* will be null if we just moved the previously opened folder */
2233 STATUSBAR_PUSH(folderview->mainwin, buf);
2234 main_window_cursor_wait(folderview->mainwin);
2236 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2237 summary_show(folderview->summaryview, NULL);
2238 folder_item_close(olditem);
2239 main_window_cursor_normal(folderview->mainwin);
2240 STATUSBAR_POP(folderview->mainwin);
2241 if (olditem->folder->klass->item_closed)
2242 olditem->folder->klass->item_closed(olditem);
2247 if (folderview->opened &&
2248 !GTK_CMCTREE_ROW(folderview->opened)->children)
2249 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2251 folderview->opened = NULL;
2253 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2254 gint column, FolderView *folderview)
2256 static gboolean can_select = TRUE; /* exclusive lock */
2261 GtkCMCTreeNode *old_opened = folderview->opened;
2263 folderview->selected = row;
2265 debug_print("newly selected %p, opened %p\n", folderview->selected,
2266 folderview->opened);
2267 if (folderview->opened == row) {
2268 folderview->open_folder = FALSE;
2273 item = gtk_cmctree_node_get_row_data(ctree, row);
2276 folderview->open_folder = FALSE;
2280 if (!can_select || summary_is_locked(folderview->summaryview)) {
2281 if (folderview->opened) {
2282 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2283 gtk_cmctree_select(ctree, folderview->opened);
2285 folderview->open_folder = FALSE;
2290 if (!folderview->open_folder) {
2297 /* Save cache for old folder */
2298 /* We don't want to lose all caches if sylpheed crashed */
2299 /* resets folderview->opened to NULL */
2300 folderview_close_opened(folderview, FALSE);
2302 /* CLAWS: set compose button type: news folder items
2303 * always have a news folder as parent */
2305 toolbar_set_compose_button
2306 (folderview->mainwin->toolbar,
2307 FOLDER_TYPE(item->folder) == F_NEWS ?
2308 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2311 debug_print("Folder %s is selected\n", item->path);
2313 if (!GTK_CMCTREE_ROW(row)->children)
2314 gtk_cmctree_expand(ctree, row);
2316 /* ungrab the mouse event */
2317 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2318 gtk_grab_remove(GTK_WIDGET(ctree));
2319 if (gdk_pointer_is_grabbed())
2320 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2324 /* TODO: wwp: avoid displaying (null) in the status bar */
2325 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2326 item->path : "(null)");
2327 debug_print("%s\n", buf);
2328 STATUSBAR_PUSH(folderview->mainwin, buf);
2331 main_window_cursor_wait(folderview->mainwin);
2333 if (folderview->scanning_folder == item->folder) {
2336 res = folder_item_open(item);
2339 if (res == -1 && item->no_select == FALSE) {
2340 main_window_cursor_normal(folderview->mainwin);
2341 STATUSBAR_POP(folderview->mainwin);
2343 alertpanel_error(_("Folder could not be opened."));
2345 folderview->open_folder = FALSE;
2349 } else if (res == -2 && item->no_select == FALSE) {
2350 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2351 data->ctree = ctree;
2353 data->column = column;
2354 data->folderview = folderview;
2355 debug_print("postponing open of %s till end of scan\n",
2356 item->path ? item->path:item->name);
2357 folderview->open_folder = FALSE;
2359 if (folderview->postpone_select_id != 0)
2360 g_source_remove(folderview->postpone_select_id);
2361 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2366 main_window_cursor_normal(folderview->mainwin);
2369 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2370 opened = summary_show(folderview->summaryview, item);
2372 folder_clean_cache_memory(item);
2375 gtkut_ctree_set_focus_row(ctree, old_opened);
2376 gtk_cmctree_select(ctree, old_opened);
2377 folderview->opened = old_opened;
2379 folderview->opened = row;
2380 if (gtk_cmctree_node_is_visible(ctree, row)
2381 != GTK_VISIBILITY_FULL)
2382 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2385 STATUSBAR_POP(folderview->mainwin);
2387 folderview->open_folder = FALSE;
2392 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2393 FolderView *folderview)
2397 item = gtk_cmctree_node_get_row_data(ctree, node);
2398 cm_return_if_fail(item != NULL);
2399 item->collapsed = FALSE;
2400 folderview_update_node(folderview, node);
2403 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2404 FolderView *folderview)
2408 item = gtk_cmctree_node_get_row_data(ctree, node);
2409 cm_return_if_fail(item != NULL);
2410 item->collapsed = TRUE;
2411 folderview_update_node(folderview, node);
2414 static void folderview_popup_close(GtkMenuShell *menu_shell,
2415 FolderView *folderview)
2417 if (!folderview->opened) return;
2419 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2422 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2423 FolderView *folderview)
2425 FolderColumnType type = folderview->col_state[column].type;
2427 prefs_common.folder_col_size[type] = width;
2430 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2432 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2433 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2434 GtkCMCTreeNode *node, *parent_node;
2435 gint *col_pos = folderview->col_pos;
2436 FolderItemUpdateData hookdata;
2438 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2439 if (parent_node == NULL)
2442 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2444 text[col_pos[F_COL_FOLDER]] = item->name;
2445 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2450 gtk_cmctree_expand(ctree, parent_node);
2451 gtk_cmctree_node_set_row_data(ctree, node, item);
2453 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2454 folderview_sort_folders(folderview, parent_node, item->folder);
2456 hookdata.item = item;
2457 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2458 hookdata.msg = NULL;
2459 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2461 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2464 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2466 FolderView *folderview = (FolderView *)data;
2468 GSList *mlist = NULL;
2470 FolderItem *special_trash = NULL;
2473 if (!folderview->selected) return;
2474 item = folderview_get_selected_item(folderview);
2475 cm_return_if_fail(item != NULL);
2476 cm_return_if_fail(item->folder != NULL);
2478 if (NULL != (ac = account_find_from_item(item)))
2479 special_trash = account_get_special_folder(ac, F_TRASH);
2481 if (item != item->folder->trash && item != special_trash
2482 && !folder_has_parent_of_type(item, F_TRASH)) return;
2484 if (prefs_common.ask_on_clean) {
2485 if (alertpanel(_("Empty trash"),
2486 _("Delete all messages in trash?"),
2487 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2491 mlist = folder_item_get_msg_list(item);
2493 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2494 MsgInfo * msginfo = (MsgInfo *) cur->data;
2495 if (MSG_IS_LOCKED(msginfo->flags))
2497 /* is it partially received? (partial_recv isn't cached) */
2498 if (msginfo->total_size != 0 &&
2499 msginfo->size != (off_t)msginfo->total_size)
2500 partial_mark_for_delete(msginfo);
2502 procmsg_msg_list_free(mlist);
2504 folder_item_remove_all_msg(item);
2507 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2509 FolderView *folderview = (FolderView *)data;
2511 FolderItem *special_queue = NULL;
2513 gchar *errstr = NULL;
2515 if (!folderview->selected) return;
2516 item = folderview_get_selected_item(folderview);
2517 cm_return_if_fail(item != NULL);
2518 cm_return_if_fail(item->folder != NULL);
2520 if (NULL != (ac = account_find_from_item(item)))
2521 special_queue = account_get_special_folder(ac, F_QUEUE);
2523 if (item != item->folder->queue && item != special_queue
2524 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2526 if (procmsg_queue_is_empty(item))
2529 if (prefs_common.work_offline)
2530 if (alertpanel(_("Offline warning"),
2531 _("You're working offline. Override?"),
2532 GTK_STOCK_NO, GTK_STOCK_YES,
2533 NULL) != G_ALERTALTERNATE)
2536 /* ask for confirmation before sending queued messages only
2537 in online mode and if there is at least one message queued
2538 in any of the folder queue
2540 if (prefs_common.confirm_send_queued_messages) {
2541 if (!prefs_common.work_offline) {
2542 if (alertpanel(_("Send queued messages"),
2543 _("Send all queued messages?"),
2544 GTK_STOCK_CANCEL, _("_Send"),
2545 NULL) != G_ALERTALTERNATE)
2550 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2552 alertpanel_error_log(_("Some errors occurred while "
2553 "sending queued messages."));
2555 alertpanel_error_log(_("Some errors occurred "
2556 "while sending queued messages:\n%s"), errstr);
2562 static void folderview_search_cb(GtkAction *action, gpointer data)
2564 FolderView *folderview = (FolderView *)data;
2565 summary_search(folderview->summaryview);
2568 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2570 FolderView *folderview = (FolderView *)data;
2573 if (!folderview->selected) return;
2575 item = folderview_get_selected_item(folderview);
2576 cm_return_if_fail(item != NULL);
2577 cm_return_if_fail(item->folder != NULL);
2579 item->processing_pending = TRUE;
2580 folder_item_apply_processing(item);
2581 item->processing_pending = FALSE;
2584 static void folderview_property_cb(GtkAction *action, gpointer data)
2586 FolderView *folderview = (FolderView *)data;
2589 if (!folderview->selected) return;
2591 item = folderview_get_selected_item(folderview);
2592 cm_return_if_fail(item != NULL);
2593 cm_return_if_fail(item->folder != NULL);
2595 prefs_folder_item_open(item);
2598 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2600 GSList *list = NULL;
2601 GSList *done = NULL;
2602 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2604 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2605 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2606 && list->data != node) {
2607 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2608 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2611 for (list = done; list != NULL; list = g_slist_next(list)) {
2612 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2618 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2619 FolderItem *to_folder, gboolean copy)
2621 FolderItem *new_folder = NULL;
2625 cm_return_if_fail(folderview != NULL);
2626 cm_return_if_fail(from_folder != NULL);
2627 cm_return_if_fail(to_folder != NULL);
2629 if (prefs_common.warn_dnd) {
2630 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2631 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2632 from_folder->name, to_folder->name);
2633 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2634 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2635 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2638 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2640 else if (status & G_ALERTDISABLE)
2641 prefs_common.warn_dnd = FALSE;
2644 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2645 from_folder->name, to_folder->name);
2646 STATUSBAR_PUSH(folderview->mainwin, buf);
2648 summary_clear_all(folderview->summaryview);
2649 folderview->opened = NULL;
2650 folderview->selected = NULL;
2651 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2653 main_window_cursor_wait(folderview->mainwin);
2655 statusbar_verbosity_set(FALSE);
2656 folder_item_update_freeze();
2657 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2658 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2659 statusbar_verbosity_set(FALSE);
2660 main_window_cursor_normal(folderview->mainwin);
2661 STATUSBAR_POP(folderview->mainwin);
2662 folder_item_update_thaw();
2663 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2665 folderview_sort_folders(folderview,
2666 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2667 NULL, to_folder), new_folder->folder);
2668 folderview_select(folderview, new_folder);
2669 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2671 statusbar_verbosity_set(FALSE);
2672 main_window_cursor_normal(folderview->mainwin);
2673 STATUSBAR_POP(folderview->mainwin);
2674 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2675 folder_item_update_thaw();
2677 case F_MOVE_FAILED_DEST_IS_PARENT:
2678 alertpanel_error(_("Source and destination are the same."));
2680 case F_MOVE_FAILED_DEST_IS_CHILD:
2681 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2682 _("Can't move a folder to one of its children."));
2684 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2685 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2688 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2693 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2696 static gint folderview_clist_compare(GtkCMCList *clist,
2697 gconstpointer ptr1, gconstpointer ptr2)
2699 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2700 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2702 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2704 return item1->order - item2->order;
2707 // if only one folder has an order it comes first
2708 if (item1->order > 0)
2712 if (item2->order > 0)
2718 return (item2->name != NULL);
2722 return g_utf8_collate(item1->name, item2->name);
2725 static void folderview_processing_cb(GtkAction *action, gpointer data)
2727 FolderView *folderview = (FolderView *)data;
2731 if (!folderview->selected) return;
2733 item = folderview_get_selected_item(folderview);
2734 cm_return_if_fail(item != NULL);
2735 cm_return_if_fail(item->folder != NULL);
2737 id = folder_item_get_identifier(item);
2738 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2741 prefs_filtering_open(&item->prefs->processing, title,
2742 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2746 void folderview_set_target_folder_color(gint color_op)
2750 FolderView *folderview;
2752 for (list = folderview_list; list != NULL; list = list->next) {
2753 folderview = (FolderView *)list->data;
2754 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2756 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2757 folderview->color_op;
2763 static gchar *last_smallfont = NULL;
2764 static gchar *last_normalfont = NULL;
2765 static gchar *last_boldfont = NULL;
2766 static gboolean last_derive = 0;
2768 void folderview_reinit_fonts(FolderView *folderview)
2771 g_free(last_smallfont);
2772 last_smallfont = NULL;
2773 g_free(last_normalfont);
2774 last_normalfont = NULL;
2775 g_free(last_boldfont);
2776 last_boldfont = NULL;
2779 void folderview_reflect_prefs(void)
2781 gboolean update_font = FALSE;
2782 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2783 FolderItem *item = folderview_get_selected_item(folderview);
2784 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2785 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2786 gint height = gtk_adjustment_get_value(pos);
2788 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2789 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2790 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2791 last_derive != prefs_common.derive_from_normal_font)
2797 g_free(last_smallfont);
2798 last_smallfont = g_strdup(SMALL_FONT);
2799 g_free(last_normalfont);
2800 last_normalfont = g_strdup(NORMAL_FONT);
2801 g_free(last_boldfont);
2802 last_boldfont = g_strdup(BOLD_FONT);
2803 last_derive = prefs_common.derive_from_normal_font;
2805 #define STYLE_FREE(s) \
2807 g_object_unref(s); \
2811 STYLE_FREE(normal_style);
2812 STYLE_FREE(normal_color_style);
2813 STYLE_FREE(bold_style);
2814 STYLE_FREE(bold_color_style);
2815 STYLE_FREE(bold_tgtfold_style);
2819 folderview_init(folderview);
2820 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2821 folderview_column_set_titles(folderview);
2822 folderview_set_all();
2824 g_signal_handlers_block_by_func
2825 (G_OBJECT(folderview->ctree),
2826 G_CALLBACK(folderview_selected), folderview);
2829 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2830 GTK_CMCTREE(folderview->ctree), NULL, item);
2832 folderview_select(folderview, item);
2833 folderview->open_folder = FALSE;
2834 folderview->selected = node;
2837 g_signal_handlers_unblock_by_func
2838 (G_OBJECT(folderview->ctree),
2839 G_CALLBACK(folderview_selected), folderview);
2841 pos = gtk_scrolled_window_get_vadjustment(
2842 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2843 gtk_adjustment_set_value(pos, height);
2844 gtk_adjustment_changed(pos);
2845 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2848 static void drag_state_stop(FolderView *folderview)
2850 if (folderview->drag_timer_id)
2851 g_source_remove(folderview->drag_timer_id);
2852 folderview->drag_timer_id = 0;
2853 folderview->drag_node = NULL;
2856 static gboolean folderview_defer_expand(FolderView *folderview)
2858 if (folderview->drag_node) {
2859 folderview_recollapse_nodes(folderview, folderview->drag_node);
2860 if (folderview->drag_item->collapsed) {
2861 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2862 folderview->nodes_to_recollapse = g_slist_append
2863 (folderview->nodes_to_recollapse, folderview->drag_node);
2866 folderview->drag_item = NULL;
2867 folderview->drag_timer_id = 0;
2871 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2873 /* the idea is that we call drag_state_start() whenever we want expansion to
2874 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2875 * we need to call drag_state_stop() */
2876 drag_state_stop(folderview);
2877 /* request expansion */
2878 if (0 != (folderview->drag_timer_id = g_timeout_add
2879 (prefs_common.hover_timeout,
2880 (GSourceFunc)folderview_defer_expand,
2882 folderview->drag_node = node;
2883 folderview->drag_item = item;
2886 #ifndef GENERIC_UMPC
2887 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2888 FolderView *folderview)
2890 GdkDragContext *context;
2892 cm_return_if_fail(folderview != NULL);
2893 if (folderview->selected == NULL) return;
2894 if (folderview->nodes_to_recollapse)
2895 g_slist_free(folderview->nodes_to_recollapse);
2896 folderview->nodes_to_recollapse = NULL;
2897 context = gtk_drag_begin(widget, folderview->target_list,
2898 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2899 gtk_drag_set_icon_default(context);
2902 static void folderview_drag_data_get(GtkWidget *widget,
2903 GdkDragContext *drag_context,
2904 GtkSelectionData *selection_data,
2907 FolderView *folderview)
2911 gchar *source = NULL;
2912 if (info == TARGET_DUMMY) {
2913 sel = GTK_CMCLIST(folderview->ctree)->selection;
2917 item = gtk_cmctree_node_get_row_data
2918 (GTK_CMCTREE(folderview->ctree),
2919 GTK_CMCTREE_NODE(sel->data));
2921 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2922 gtk_selection_data_set(selection_data,
2923 gtk_selection_data_get_target(selection_data), 8,
2924 source, strlen(source));
2927 g_warning("unknown info %d", info);
2931 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2933 FolderUpdateData *hookdata;
2934 FolderView *folderview;
2938 folderview = (FolderView *) userdata;
2939 cm_return_val_if_fail(hookdata != NULL, FALSE);
2940 cm_return_val_if_fail(folderview != NULL, FALSE);
2942 ctree = folderview->ctree;
2943 cm_return_val_if_fail(ctree != NULL, FALSE);
2945 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2946 folderview_create_folder_node(folderview, hookdata->item);
2947 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2948 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2949 NULL, folder_item_parent(hookdata->item));
2950 folderview_sort_folders(folderview, node, hookdata->folder);
2951 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2952 GtkCMCTreeNode *node;
2954 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2956 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2957 if (folderview->selected == node)
2958 folderview->selected = NULL;
2959 if (folderview->opened == node)
2960 folderview->opened = NULL;
2962 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2963 /* do nothing, it's done by the ADD and REMOVE) */
2964 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2965 folderview_set(folderview);
2970 static gboolean folderview_dnd_scroll_cb(gpointer data)
2972 FolderView *folderview = (FolderView *)data;
2973 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2974 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2975 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2976 gint max = (int)gtk_adjustment_get_upper(pos) -
2977 (int)gtk_adjustment_get_page_size(pos);
2979 if (folderview->scroll_value == 0) {
2980 folderview->scroll_timeout_id = 0;
2984 if (folderview->scroll_value > 0 && new_val > max) {
2986 } else if (folderview->scroll_value < 0 && new_val < 0) {
2989 gtk_adjustment_set_value(pos, new_val);
2994 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2995 GdkDragContext *context,
2999 FolderView *folderview)
3002 FolderItem *item = NULL, *src_item = NULL;
3003 GtkCMCTreeNode *node = NULL;
3004 gboolean acceptable = FALSE;
3005 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3006 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3007 int height = (int)gtk_adjustment_get_page_size(pos);
3008 int total_height = (int)gtk_adjustment_get_upper(pos);
3009 int vpos = (int)gtk_adjustment_get_value(pos);
3010 int offset = prefs_common.show_col_headers ? 24:0;
3013 if (gtk_cmclist_get_selection_info
3014 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3015 GtkWidget *srcwidget;
3017 if (y > height - (48 - offset) && height + vpos < total_height) {
3018 dist = -(height - (48 - offset) - y);
3019 folderview->scroll_value = 1.41f * (1+(dist / 6));
3020 } else if (y < 72 - (24 - offset) && y >= 0) {
3021 dist = 72 - (24 - offset) - y;
3022 folderview->scroll_value = -1.41f * (1+(dist / 6));
3024 folderview->scroll_value = 0;
3026 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3027 folderview->scroll_timeout_id =
3028 g_timeout_add(30, folderview_dnd_scroll_cb,
3032 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3033 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3034 src_item = folderview->summaryview->folder_item;
3036 srcwidget = gtk_drag_get_source_widget(context);
3037 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3038 /* comes from summaryview */
3039 /* we are copying messages, so only accept folder items that are not
3040 the source item, are no root items and can copy messages */
3041 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3042 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3043 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3045 } else if (srcwidget == folderview->ctree) {
3046 /* comes from folderview */
3047 /* we are moving folder items, only accept folders that are not
3048 the source items and can copy messages and create folder items */
3049 if (item && item->folder && src_item && src_item != item &&
3050 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3051 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3052 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3053 || item->folder == src_item->folder))
3056 /* comes from another app */
3057 /* we are adding messages, so only accept folder items that are
3058 no root items and can copy messages */
3059 if (item && item->folder && folder_item_parent(item) != NULL
3060 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3061 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3066 if (acceptable || (src_item && src_item == item))
3067 drag_state_start(folderview, node, item);
3070 g_signal_handlers_block_by_func
3072 G_CALLBACK(folderview_selected), folderview);
3073 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3074 g_signal_handlers_unblock_by_func
3076 G_CALLBACK(folderview_selected), folderview);
3077 gdk_drag_status(context,
3078 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3079 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3081 if (folderview->opened)
3082 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3083 gdk_drag_status(context, 0, time);
3089 static void folderview_drag_leave_cb(GtkWidget *widget,
3090 GdkDragContext *context,
3092 FolderView *folderview)
3094 drag_state_stop(folderview);
3095 folderview->scroll_value = 0;
3096 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3099 static void free_info (gpointer stuff, gpointer data)
3104 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3105 guint time, FolderItem *item)
3108 GSList *msglist = NULL;
3109 list = uri_list_extract_filenames(data);
3110 if (!(item && item->folder && folder_item_parent(item) != NULL
3111 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3113 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3114 debug_print("item doesn't fit\n");
3118 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3119 debug_print("list is empty\n");
3122 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3123 MsgFileInfo *info = NULL;
3125 if (file_is_email((gchar *)tmp->data)) {
3126 info = g_new0(MsgFileInfo, 1);
3127 info->msginfo = NULL;
3128 info->file = (gchar *)tmp->data;
3129 msglist = g_slist_prepend(msglist, info);
3130 debug_print("file is a mail\n");
3132 debug_print("file isn't a mail\n");
3136 msglist = g_slist_reverse(msglist);
3137 folder_item_add_msgs(item, msglist, FALSE);
3138 g_slist_foreach(msglist, free_info, NULL);
3139 g_slist_free(msglist);
3140 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3142 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3144 list_free_strings(list);
3148 static void folderview_drag_received_cb(GtkWidget *widget,
3149 GdkDragContext *drag_context,
3152 GtkSelectionData *data,
3155 FolderView *folderview)
3158 FolderItem *item = NULL, *src_item;
3159 GtkCMCTreeNode *node;
3160 int offset = prefs_common.show_col_headers ? 24:0;
3162 folderview->scroll_value = 0;
3164 if (info == TARGET_DUMMY) {
3165 drag_state_stop(folderview);
3166 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3167 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3168 /* comes from summaryview */
3169 if (gtk_cmclist_get_selection_info
3170 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3173 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3174 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3175 src_item = folderview->summaryview->folder_item;
3177 if (item->no_select) {
3178 alertpanel_error(_("The destination folder can only be used to "
3179 "store subfolders."));
3182 /* re-check (due to acceptable possibly set for folder moves */
3183 if (!(item && item->folder && item->path && !item->no_select &&
3184 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3187 if (item && src_item) {
3188 switch (gdk_drag_context_get_selected_action(drag_context)) {
3189 case GDK_ACTION_COPY:
3190 summary_copy_selected_to(folderview->summaryview, item);
3191 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3193 case GDK_ACTION_MOVE:
3194 case GDK_ACTION_DEFAULT:
3196 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3197 summary_copy_selected_to(folderview->summaryview, item);
3199 summary_move_selected_to(folderview->summaryview, item);
3200 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3203 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3205 /* comes from folderview */
3207 gboolean folder_is_normal = TRUE;
3208 gboolean copy = (GDK_ACTION_COPY ==
3209 gdk_drag_context_get_selected_action(drag_context));
3211 source = (char *)gtk_selection_data_get_data(data) + 17;
3212 if (gtk_cmclist_get_selection_info
3213 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3215 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3218 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3219 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3220 src_item = folder_find_item_from_identifier(source);
3224 src_item->stype == F_NORMAL &&
3225 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3226 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3227 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3228 !folder_has_parent_of_type(src_item, F_TRASH);
3229 if (!item || !src_item || !folder_is_normal) {
3230 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3234 folderview_move_folder(folderview, src_item, item, copy);
3235 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3237 folderview->nodes_to_recollapse = NULL;
3238 } else if (info == TARGET_MAIL_URI_LIST) {
3239 if (gtk_cmclist_get_selection_info
3240 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3243 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3245 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3246 debug_print("no node\n");
3249 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3251 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3252 debug_print("no item\n");
3255 folderview_finish_dnd(gtk_selection_data_get_data(data),
3256 drag_context, time, item);
3260 static void folderview_drag_end_cb(GtkWidget *widget,
3261 GdkDragContext *drag_context,
3262 FolderView *folderview)