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 (prefs_common.ask_mark_all_read) {
880 val = alertpanel_full(title, message,
881 GTK_STOCK_NO, GTK_STOCK_YES, NULL,
882 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
884 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
886 else if (val & G_ALERTDISABLE)
887 prefs_common.ask_mark_all_read = FALSE;
890 folder_item_update_freeze();
891 if (folderview->summaryview->folder_item != item && !recursive)
892 summary_lock(folderview->summaryview);
894 summary_freeze(folderview->summaryview);
898 folderutils_mark_all_read_recursive(item);
900 folderutils_mark_all_read(item);
903 folderutils_mark_all_unread_recursive(item);
905 folderutils_mark_all_unread(item);
907 if (folderview->summaryview->folder_item != item && !recursive)
908 summary_unlock(folderview->summaryview);
910 summary_thaw(folderview->summaryview);
911 folder_item_update_thaw();
914 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
916 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
918 cm_return_if_fail(node != NULL);
920 if (folderview->open_folder) {
924 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
925 gtkut_ctree_expand_parent_all(ctree, node);
927 folderview->open_folder = TRUE;
928 gtkut_ctree_set_focus_row(ctree, node);
929 gtk_cmctree_select(ctree, node);
930 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
931 if ((folderview->summaryview->folder_item &&
932 folderview->summaryview->folder_item->total_msgs > 0) ||
933 prefs_common.layout_mode == SMALL_LAYOUT)
934 summary_select_node(folderview->summaryview,
935 folderview->summaryview->selected, OPEN_SELECTED_ON_FOLDER_OPEN);
937 gtk_widget_grab_focus(folderview->ctree);
940 void folderview_unselect(FolderView *folderview)
942 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
944 (GTK_CMCTREE(folderview->ctree), folderview->opened);
946 folderview->selected = folderview->opened = NULL;
949 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
950 GtkCMCTreeNode *node,
956 node = gtkut_ctree_node_next(ctree, node);
958 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
960 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
961 item = gtk_cmctree_node_get_row_data(ctree, node);
964 if (item->stype == F_TRASH || item->stype == F_DRAFT)
968 if(item->unread_msgs > 0)
972 if(item->new_msgs > 0)
976 if(item->marked_msgs > 0)
980 if(item->total_msgs > 0)
989 void folderview_select_next_with_flag(FolderView *folderview,
992 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
993 GtkCMCTreeNode *node = NULL;
994 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
998 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
1001 prefs_common.summary_select_prio[0] = ACTION_NEW;
1004 prefs_common.summary_select_prio[0] = ACTION_MARKED;
1007 prefs_common.summary_select_prio[0] = ACTION_FIRST_LIST;
1011 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
1013 folderview_select_node(folderview, node);
1017 if (!folderview->opened ||
1018 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
1022 /* search again from the first node */
1023 node = folderview_find_next_with_flag(ctree, NULL, flag);
1025 folderview_select_node(folderview, node);
1028 prefs_common.summary_select_prio[0] = last_summary_select_prio;
1031 FolderItem *folderview_get_selected_item(FolderView *folderview)
1033 g_return_val_if_fail(folderview != NULL, NULL);
1034 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1036 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1038 if (!folderview->selected) return NULL;
1039 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
1042 FolderItem *folderview_get_opened_item(FolderView *folderview)
1044 g_return_val_if_fail(folderview != NULL, NULL);
1045 g_return_val_if_fail(folderview->ctree != NULL, NULL);
1047 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1049 if (!folderview->opened) return NULL;
1050 return gtk_cmctree_node_get_row_data(ctree, folderview->opened);
1053 static void folderview_set_folders(FolderView *folderview)
1056 list = folder_get_list();
1058 for (; list != NULL; list = list->next) {
1059 folderview_append_folder(folderview, FOLDER(list->data));
1063 static gchar *get_scan_str(FolderItem *item)
1066 return g_strdup_printf(_("Scanning folder %s/%s..."),
1067 item->folder->name, item->path);
1069 return g_strdup_printf(_("Scanning folder %s..."),
1070 item->folder->name);
1072 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1076 for (list = folderview_list; list != NULL; list = list->next) {
1077 FolderView *folderview = (FolderView *)list->data;
1078 MainWindow *mainwin = folderview->mainwin;
1079 gchar *str = get_scan_str(item);
1081 STATUSBAR_PUSH(mainwin, str);
1082 STATUSBAR_POP(mainwin);
1087 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1090 MainWindow *mainwin = mainwindow_get_mainwindow();
1091 FolderView *folderview = NULL;
1092 GtkAdjustment *pos = NULL;
1095 cm_return_if_fail(folder != NULL);
1097 if (!folder->klass->scan_tree) return;
1100 alertpanel_full(_("Rebuild folder tree"),
1101 _("Rebuilding the folder tree will remove "
1102 "local caches. Do you want to continue?"),
1103 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1104 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1105 != G_ALERTALTERNATE) {
1111 window = label_window_create(_("Rebuilding folder tree..."));
1113 window = label_window_create(_("Scanning folder tree..."));
1116 folderview = mainwin->folderview;
1119 pos = gtk_scrolled_window_get_vadjustment(
1120 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1121 height = gtk_adjustment_get_value(pos);
1124 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1125 folder_scan_tree(folder, rebuild);
1126 folder_set_ui_func(folder, NULL, NULL);
1128 folderview_set_all();
1131 pos = gtk_scrolled_window_get_vadjustment(
1132 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1133 gtk_adjustment_set_value(pos, height);
1134 gtk_adjustment_changed(pos);
1136 label_window_destroy(window);
1140 /** folderview_check_new()
1141 * Scan and update the folder and return the
1142 * count the number of new messages since last check.
1143 * \param folder the folder to check for new messages
1144 * \return the number of new messages since last check
1146 gint folderview_check_new(Folder *folder)
1150 FolderView *folderview;
1152 GtkCMCTreeNode *node;
1154 gint former_new_msgs = 0;
1155 gint former_new = 0, former_unread = 0, former_total;
1157 for (list = folderview_list; list != NULL; list = list->next) {
1158 folderview = (FolderView *)list->data;
1159 ctree = GTK_CMCTREE(folderview->ctree);
1160 folderview->scanning_folder = folder;
1162 main_window_lock(folderview->mainwin);
1164 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1165 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1167 item = gtk_cmctree_node_get_row_data(ctree, node);
1168 if (!item || !item->path || !item->folder) continue;
1169 if (item->no_select) continue;
1170 if (folder && folder != item->folder) continue;
1171 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1172 if (!item->prefs->newmailcheck) continue;
1173 if (item->processing_pending == TRUE) {
1174 debug_print("skipping %s, processing pending\n",
1175 item->path ? item->path : item->name);
1178 if (item->scanning != ITEM_NOT_SCANNING) {
1179 debug_print("skipping %s, scanning\n",
1180 item->path ? item->path : item->name);
1184 str = get_scan_str(item);
1186 STATUSBAR_PUSH(folderview->mainwin, str);
1190 folderview_scan_tree_func(item->folder, item, NULL);
1191 former_new = item->new_msgs;
1192 former_unread = item->unread_msgs;
1193 former_total = item->total_msgs;
1195 if (item->folder->klass->scan_required &&
1196 (item->folder->klass->scan_required(item->folder, item) ||
1197 item->folder->inbox == item ||
1198 item->opened == TRUE ||
1199 item->processing_pending == TRUE)) {
1200 if (folder_item_scan(item) < 0) {
1202 summaryview_unlock(folderview->summaryview, item);
1203 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1204 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1205 item->path ? item->path:item->name);
1206 STATUSBAR_POP(folderview->mainwin);
1208 } else if (!FOLDER_IS_LOCAL(folder)) {
1209 STATUSBAR_POP(folderview->mainwin);
1214 } else if (!item->folder->klass->scan_required) {
1215 if (folder_item_scan(item) < 0) {
1216 summaryview_unlock(folderview->summaryview, item);
1217 if (folder && !FOLDER_IS_LOCAL(folder)) {
1218 STATUSBAR_POP(folderview->mainwin);
1223 if (former_new != item->new_msgs ||
1224 former_unread != item->unread_msgs ||
1225 former_total != item->total_msgs)
1226 folderview_update_node(folderview, node);
1228 new_msgs += item->new_msgs;
1229 former_new_msgs += former_new;
1230 STATUSBAR_POP(folderview->mainwin);
1232 folderview->scanning_folder = NULL;
1233 main_window_unlock(folderview->mainwin);
1237 folder_write_list();
1238 /* Number of new messages since last check is the just the difference
1239 * between former_new_msgs and new_msgs. If new_msgs is less than
1240 * former_new_msgs, that would mean another session accessed the folder
1241 * and the result is not well defined.
1243 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1247 void folderview_check_new_all(void)
1251 FolderView *folderview;
1253 folderview = (FolderView *)folderview_list->data;
1256 main_window_lock(folderview->mainwin);
1257 window = label_window_create
1258 (_("Checking for new messages in all folders..."));
1260 list = folder_get_list();
1261 for (; list != NULL; list = list->next) {
1262 Folder *folder = list->data;
1264 folderview_check_new(folder);
1267 folder_write_list();
1268 folderview_set_all();
1270 label_window_destroy(window);
1271 main_window_unlock(folderview->mainwin);
1275 static gboolean folderview_have_children_sub(FolderView *folderview,
1281 if (!item || !item->folder || !item->folder->node)
1284 node = item->folder->node;
1286 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1287 node = node->children;
1289 if (in_sub && item->total_msgs > 0) {
1293 while (node != NULL) {
1294 if (node && node->data) {
1295 FolderItem *next_item = (FolderItem*) node->data;
1297 if (folderview_have_children_sub(folderview,
1306 static gboolean folderview_have_children(FolderView *folderview,
1309 return folderview_have_children_sub(folderview, item, FALSE);
1312 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1318 if (!item || !item->folder || !item->folder->node)
1321 node = item->folder->node;
1323 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1324 node = node->children;
1327 (item->new_msgs > 0 ||
1328 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1332 while (node != NULL) {
1333 if (node && node->data) {
1334 FolderItem *next_item = (FolderItem*) node->data;
1336 if (folderview_have_new_children_sub(folderview,
1345 static gboolean folderview_have_new_children(FolderView *folderview,
1348 return folderview_have_new_children_sub(folderview, item, FALSE);
1351 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1357 if (!item || !item->folder || !item->folder->node)
1360 node = item->folder->node;
1362 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1363 node = node->children;
1366 (item->unread_msgs > 0 ||
1367 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1371 while (node != NULL) {
1372 if (node && node->data) {
1373 FolderItem *next_item = (FolderItem*) node->data;
1375 if (folderview_have_unread_children_sub(folderview,
1385 static gboolean folderview_have_unread_children(FolderView *folderview,
1388 return folderview_have_unread_children_sub(folderview, item, FALSE);
1391 static gboolean folderview_have_read_children_sub(FolderView *folderview,
1397 if (!item || !item->folder || !item->folder->node) {
1401 node = item->folder->node;
1403 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1404 node = node->children;
1407 (((item->total_msgs > 0) &&
1408 (item->unread_msgs != (item->total_msgs - item->ignored_msgs))))) {
1412 while (node != NULL) {
1413 if (node && node->data) {
1414 FolderItem *next_item = (FolderItem*) node->data;
1416 if (folderview_have_read_children_sub(folderview,
1427 static gboolean folderview_have_read_children(FolderView *folderview,
1430 return folderview_have_read_children_sub(folderview, item, FALSE);
1433 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1439 if (!item || !item->folder || !item->folder->node)
1442 node = item->folder->node;
1444 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1445 node = node->children;
1447 if (in_sub && item->search_match){
1451 while (node != NULL) {
1452 if (node && node->data) {
1453 FolderItem *next_item = (FolderItem*) node->data;
1455 if (folderview_have_matching_children_sub(folderview,
1465 static gboolean folderview_have_matching_children(FolderView *folderview,
1468 return folderview_have_matching_children_sub(folderview, item, FALSE);
1471 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1477 if (!item || !item->folder || !item->folder->node)
1480 node = item->folder->node;
1482 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1483 node = node->children;
1485 if (item->marked_msgs != 0) {
1489 while (node != NULL) {
1490 if (node && node->data) {
1491 FolderItem *next_item = (FolderItem*) node->data;
1493 if (folderview_have_marked_children_sub(folderview,
1502 static gboolean folderview_have_marked_children(FolderView *folderview,
1505 return folderview_have_marked_children_sub(folderview, item, FALSE);
1508 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1510 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1511 GtkStyle *style = NULL;
1512 GtkStyle *color_style = NULL;
1514 GdkPixbuf *xpm, *openxpm;
1515 static GdkPixbuf *searchicon;
1516 gboolean mark = FALSE;
1519 gboolean add_unread_mark;
1520 gboolean add_sub_match_mark;
1521 gboolean use_bold, use_color;
1522 gint *col_pos = folderview->col_pos;
1523 SpecialFolderItemType stype;
1525 item = gtk_cmctree_node_get_row_data(ctree, node);
1526 cm_return_if_fail(item != NULL);
1528 if (!GTK_CMCTREE_ROW(node)->expanded)
1529 mark = folderview_have_marked_children(folderview, item);
1531 mark = (item->marked_msgs != 0);
1533 stype = item->stype;
1534 if (stype == F_NORMAL) {
1535 if (folder_has_parent_of_type(item, F_TRASH))
1537 else if (folder_has_parent_of_type(item, F_DRAFT))
1539 else if (folder_has_parent_of_type(item, F_OUTBOX))
1541 else if (folder_has_parent_of_type(item, F_QUEUE))
1546 if (item->hide_read_msgs || item->hide_read_threads) {
1547 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1548 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1550 xpm = mark?m_inboxxpm:inboxxpm;
1551 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1555 if (item->hide_read_msgs || item->hide_read_threads) {
1556 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1557 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1559 xpm = mark?m_outboxxpm:outboxxpm;
1560 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1564 if (item->hide_read_msgs || item->hide_read_threads) {
1565 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1566 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1568 xpm = mark?m_queuexpm:queuexpm;
1569 openxpm = mark?m_queueopenxpm:queueopenxpm;
1573 if (item->hide_read_msgs || item->hide_read_threads) {
1574 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1575 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1577 xpm = mark?m_trashxpm:trashxpm;
1578 openxpm = mark?m_trashopenxpm:trashopenxpm;
1582 xpm = mark?m_draftsxpm:draftsxpm;
1583 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1587 FOLDER_TYPE(item->folder) == F_IMAP &&
1588 item->folder->account->imap_subsonly) {
1589 xpm = mark?m_foldersubsxpm:foldersubsxpm;
1590 openxpm = foldersubsopenxpm;
1591 } else if (item->no_select) {
1592 xpm = mark?m_foldernoselectxpm:foldernoselectxpm;
1593 openxpm = foldernoselectopenxpm;
1594 } else if (item->hide_read_msgs || item->hide_read_threads) {
1595 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1596 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1598 xpm = mark?m_folderxpm:folderxpm;
1599 openxpm = mark?m_folderopenxpm:folderopenxpm;
1603 name = folder_item_get_name(item);
1605 if (!GTK_CMCTREE_ROW(node)->expanded) {
1606 add_unread_mark = folderview_have_unread_children(
1608 add_sub_match_mark = folderview_have_matching_children(
1611 add_unread_mark = FALSE;
1612 add_sub_match_mark = FALSE;
1615 if (item->search_match) {
1617 stock_pixbuf_gdk(STOCK_PIXMAP_QUICKSEARCH,
1620 xpm = openxpm = searchicon;
1624 if (prefs_common.display_folder_unread) {
1625 if (folder_has_parent_of_type(item, F_QUEUE)) {
1626 /* only total_msgs matters here */
1627 if (item->total_msgs > 0) {
1628 /* show total number (should be equal to the unread number)
1630 str = g_strdup_printf("%s (%d%s%s)",
1631 name, item->total_msgs,
1632 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1633 (item->unreadmarked_msgs > 0) ? "!" : "");
1636 if (prefs_common.display_folder_unread == 1) {
1637 if (item->unread_msgs > 0) {
1638 /* show unread number and signs */
1639 str = g_strdup_printf("%s (%d%s%s)",
1640 name, item->unread_msgs,
1641 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1642 (item->unreadmarked_msgs > 0) ? "!" : "");
1645 if (item->total_msgs > 0) {
1646 /* show unread number, total number and signs if any */
1647 str = g_strdup_printf("%s (%d/%d%s%s)",
1648 name, item->unread_msgs, item->total_msgs,
1649 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1650 (item->unreadmarked_msgs > 0) ? "!" : "");
1654 if ((str == NULL) &&
1655 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1656 /* no unread/total numbers, but at least one sign */
1657 str = g_strdup_printf("%s (%s%s)",
1659 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1660 (item->unreadmarked_msgs > 0) ? "!" : "");
1664 /* last fallback, folder name only or with +! sign */
1665 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1666 str = g_strdup_printf("%s%s",
1668 } else if (item->unreadmarked_msgs > 0) {
1669 str = g_strdup_printf("%s%s",
1671 } else if (add_sub_match_mark) {
1672 str = g_strdup_printf("%s%s",
1675 str = g_strdup_printf("%s", name);
1678 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1680 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1684 if (!folder_item_parent(item)) {
1685 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1686 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1687 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1689 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1690 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1691 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1694 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1695 folder_has_parent_of_type(item, F_TRASH)) {
1696 use_bold = use_color = FALSE;
1697 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1698 GSList *list = folder_item_get_msg_list(item);
1700 use_bold = use_color = FALSE;
1701 for (cur = list; cur; cur = cur->next) {
1702 MsgInfo *msginfo = (MsgInfo *)cur->data;
1703 if (!MSG_IS_DELETED(msginfo->flags)) {
1704 /* highlight queue folder if there are any messages */
1705 use_bold = use_color = TRUE;
1709 if (!GTK_CMCTREE_ROW(node)->expanded &&
1710 use_bold == FALSE &&
1711 folderview_have_children(folderview, item))
1712 use_bold = use_color = TRUE;
1713 procmsg_msg_list_free(list);
1715 /* if unread messages exist, print with bold font */
1716 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1718 /* if new messages exist, print with colored letter */
1720 (item->new_msgs > 0) ||
1722 folderview_have_new_children(folderview, item));
1725 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1730 if (item->prefs->color > 0 && !use_color) {
1731 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1732 color_style = gtk_style_copy(bold_style);
1733 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1734 style = color_style;
1735 } else if (use_color) {
1736 style = bold_color_style;
1739 if (item->op_count > 0) {
1740 style = bold_tgtfold_style;
1742 } else if (use_color) {
1743 style = normal_color_style;
1744 gtk_cmctree_node_set_foreground(ctree, node,
1745 &folderview->color_new);
1746 } else if (item->op_count > 0) {
1747 style = bold_tgtfold_style;
1748 } else if (item->prefs->color > 0) {
1750 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1751 color_style = gtk_style_copy(normal_style);
1752 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1753 style = color_style;
1755 style = normal_style;
1758 gtk_cmctree_node_set_row_style(ctree, node, style);
1760 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1761 folderview_update_node(folderview, node);
1764 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1767 FolderView *folderview;
1769 GtkCMCTreeNode *node;
1771 cm_return_if_fail(item != NULL);
1773 for (list = folderview_list; list != NULL; list = list->next) {
1774 folderview = (FolderView *)list->data;
1775 ctree = GTK_CMCTREE(folderview->ctree);
1777 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1778 if (node && item->search_match != matches) {
1779 item->search_match = matches;
1780 folderview_update_node(folderview, node);
1785 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1787 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1788 FolderView *folderview = (FolderView *)data;
1790 GtkCMCTreeNode *node;
1791 cm_return_val_if_fail(update_info != NULL, TRUE);
1792 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1793 cm_return_val_if_fail(folderview != NULL, FALSE);
1795 ctree = GTK_CMCTREE(folderview->ctree);
1797 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1800 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1801 folderview_update_node(folderview, node);
1803 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1804 update_info->item == folderview->summaryview->folder_item &&
1805 update_info->item != NULL)
1806 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1807 summary_show(folderview->summaryview, update_info->item);
1813 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1814 GNode *gnode, GtkCMCTreeNode *cnode,
1817 FolderView *folderview = (FolderView *)data;
1818 FolderItem *item = FOLDER_ITEM(gnode->data);
1820 cm_return_val_if_fail(item != NULL, FALSE);
1822 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1823 folderview_update_node(folderview, cnode);
1828 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1831 FolderView *folderview = (FolderView *)data;
1834 if (GTK_CMCTREE_ROW(node)->children) {
1835 item = gtk_cmctree_node_get_row_data(ctree, node);
1836 cm_return_if_fail(item != NULL);
1838 if (!item->collapsed)
1839 gtk_cmctree_expand(ctree, node);
1841 folderview_update_node(folderview, node);
1845 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1846 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1849 GtkCMCTreeNode *node, *parent, *sibling;
1851 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1853 g_warning("%s not found.", item->path);
1855 parent = GTK_CMCTREE_ROW(node)->parent;
1856 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1857 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1859 sibling = GTK_CMCTREE_ROW(parent)->children;
1863 tmp = gtk_cmctree_node_get_row_data
1865 if (tmp && tmp->stype != F_NORMAL)
1866 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1870 if (node != sibling)
1871 gtk_cmctree_move(ctree, node, parent, sibling);
1878 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1881 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1882 GtkCMCTreeNode *prev = NULL;
1884 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1885 gtk_sctree_sort_recursive(ctree, root);
1886 if (root && GTK_CMCTREE_ROW(root)->parent) {
1887 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1890 set_special_folder(ctree, folder->inbox, root, &prev);
1891 set_special_folder(ctree, folder->outbox, root, &prev);
1892 set_special_folder(ctree, folder->draft, root, &prev);
1893 set_special_folder(ctree, folder->queue, root, &prev);
1894 set_special_folder(ctree, folder->trash, root, &prev);
1895 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1898 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1900 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1901 GtkCMCTreeNode *root;
1903 cm_return_if_fail(folder != NULL);
1905 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1906 folderview_gnode_func, folderview);
1907 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1909 folderview_sort_folders(folderview, root, folder);
1912 /* callback functions */
1913 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1914 GdkEventButton *event)
1918 FolderViewPopup *fpopup;
1919 GtkActionGroup *action_group;
1921 FolderItem *special_trash = NULL, *special_queue = NULL;
1923 GtkUIManager *ui_manager = gtk_ui_manager_new();
1925 if (folderview->ui_manager)
1926 g_object_unref(folderview->ui_manager);
1928 folderview->ui_manager = ui_manager;
1929 item = folderview_get_selected_item(folderview);
1931 cm_return_if_fail(item != NULL);
1932 cm_return_if_fail(item->folder != NULL);
1933 folder = item->folder;
1935 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1938 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1940 fpopup = g_hash_table_lookup(folderview_popups, "common");
1941 action_group = g_hash_table_lookup(folderview->popups, "common");
1944 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1945 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1946 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1948 if (fpopup->add_menuitems)
1949 fpopup->add_menuitems(ui_manager, item);
1951 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1952 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnread", "FolderViewPopup/MarkAllUnread", GTK_UI_MANAGER_MENUITEM)
1953 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1954 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllUnreadRec", "FolderViewPopup/MarkAllUnreadRec", GTK_UI_MANAGER_MENUITEM)
1955 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1956 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1957 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1958 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1959 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1961 if (fpopup->set_sensitivity != NULL)
1962 fpopup->set_sensitivity(ui_manager, item);
1964 if (NULL != (ac = account_find_from_item(item))) {
1965 special_trash = account_get_special_folder(ac, F_TRASH);
1966 special_queue = account_get_special_folder(ac, F_QUEUE);
1969 if ((item == folder->trash || item == special_trash
1970 || folder_has_parent_of_type(item, F_TRASH))) {
1971 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1972 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1975 if ((item == folder->queue || item == special_queue
1976 || folder_has_parent_of_type(item, F_QUEUE))) {
1977 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1978 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1981 #define SET_SENS(name, sens) \
1982 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1984 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs > 0);
1985 SET_SENS("FolderViewPopup/MarkAllUnread", (item->total_msgs > 0) &&
1986 (item->unread_msgs != (item->total_msgs - item->ignored_msgs)));
1987 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1988 SET_SENS("FolderViewPopup/MarkAllUnreadRec", folderview_have_read_children(folderview,item));
1989 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs > 0 &&
1990 folderview->selected == folderview->opened);
1991 SET_SENS("FolderViewPopup/Properties", TRUE);
1993 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1994 item->total_msgs >= 1 && !item->processing_pending);
1995 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1996 !item->no_select && !item->processing_pending);
1998 if (item == folder->trash || item == special_trash
1999 || folder_has_parent_of_type(item, F_TRASH)) {
2000 GSList *msglist = folder_item_get_msg_list(item);
2001 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
2002 procmsg_msg_list_free(msglist);
2004 if (item == folder->queue || item == special_queue
2005 || folder_has_parent_of_type(item, F_QUEUE)) {
2006 GSList *msglist = folder_item_get_msg_list(item);
2007 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
2008 procmsg_msg_list_free(msglist);
2012 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
2013 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
2014 g_signal_connect(G_OBJECT(popup), "selection_done",
2015 G_CALLBACK(folderview_popup_close),
2017 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
2018 event->button, event->time);
2021 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
2022 FolderView *folderview)
2024 GtkCMCList *clist = GTK_CMCLIST(ctree);
2025 gint prev_row = -1, row = -1, column = -1;
2027 if (!event) return FALSE;
2029 if (event->button == 1 || event->button == 2) {
2030 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
2031 folderview->open_folder = TRUE;
2033 if (event->type == GDK_2BUTTON_PRESS) {
2034 if (clist->selection) {
2035 GtkCMCTreeNode *node;
2037 node = GTK_CMCTREE_NODE(clist->selection->data);
2039 gtk_cmctree_toggle_expansion(
2042 folderview->open_folder = FALSE;
2049 if (event->button == 2 || event->button == 3) {
2051 if (clist->selection) {
2052 GtkCMCTreeNode *node;
2054 node = GTK_CMCTREE_NODE(clist->selection->data);
2056 prev_row = gtkut_ctree_get_nth_from_node
2057 (GTK_CMCTREE(ctree), node);
2060 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
2063 if (prev_row != row) {
2064 gtk_cmclist_unselect_all(clist);
2065 if (event->button == 2)
2066 folderview_select_node
2068 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
2071 gtk_cmclist_select_row(clist, row, column);
2075 if (event->button != 3) return FALSE;
2077 folderview_set_sens_and_popup_menu(folderview, row, event);
2081 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
2082 FolderView *folderview)
2084 int row = -1, column = -1;
2086 if (!event) return FALSE;
2088 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
2091 if (event->button == 1 && folderview->open_folder == FALSE &&
2092 folderview->opened != NULL) {
2093 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
2094 folderview->opened);
2095 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
2101 #define BREAK_ON_MODIFIER_KEY() \
2102 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
2104 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
2105 FolderView *folderview)
2107 GtkCMCTreeNode *node;
2110 if (!event) return FALSE;
2112 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2115 switch (event->keyval) {
2117 if (folderview->selected) {
2118 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2119 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2120 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2121 folderview->selected);
2123 folderview_select_node(folderview,
2124 folderview->selected);
2128 case GDK_KEY_Return:
2129 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2130 gtk_cmctree_toggle_expansion(
2131 GTK_CMCTREE(folderview->ctree),
2132 folderview->selected);
2136 case GDK_KEY_Return:
2137 case GDK_KEY_KP_Enter:
2138 if (folderview->selected)
2139 folderview_select_node(folderview, folderview->selected);
2143 BREAK_ON_MODIFIER_KEY();
2144 if (folderview->selected) {
2145 if (folderview->opened == folderview->selected &&
2146 (!folderview->summaryview->folder_item ||
2147 folderview->summaryview->folder_item->total_msgs == 0))
2148 folderview_select_next_with_flag(folderview, MSG_UNREAD);
2150 folderview_select_node(folderview,
2151 folderview->selected);
2155 if (folderview->selected) {
2156 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2157 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2158 folderview->selected);
2160 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2161 folderview->selected))) {
2162 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2163 NULL, folder_item_parent(item)))) {
2164 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2165 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2166 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2175 if (event->keyval == GDK_KEY_Home)
2176 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2178 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2179 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2181 gtk_sctree_select(GTK_SCTREE(folderview->ctree), node);
2183 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2184 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2194 typedef struct _PostponedSelectData
2197 GtkCMCTreeNode *row;
2199 FolderView *folderview;
2200 } PostponedSelectData;
2202 static gboolean postpone_select(void *data)
2204 PostponedSelectData *psdata = (PostponedSelectData *)data;
2205 debug_print("trying again\n");
2207 psdata->folderview->postpone_select_id = 0;
2208 psdata->folderview->open_folder = TRUE;
2209 main_window_cursor_normal(psdata->folderview->mainwin);
2210 STATUSBAR_POP(psdata->folderview->mainwin);
2211 folderview_selected(psdata->ctree, psdata->row,
2212 psdata->column, psdata->folderview);
2217 void folderview_close_opened(FolderView *folderview, gboolean dirty)
2219 if (folderview->opened) {
2221 folderview->opened = NULL;
2225 FolderItem *olditem =
2226 gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2227 folderview->opened);
2229 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2230 olditem->path ? olditem->path:olditem->name);
2231 /* will be null if we just moved the previously opened folder */
2232 STATUSBAR_PUSH(folderview->mainwin, buf);
2233 main_window_cursor_wait(folderview->mainwin);
2235 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2236 summary_show(folderview->summaryview, NULL);
2237 folder_item_close(olditem);
2238 main_window_cursor_normal(folderview->mainwin);
2239 STATUSBAR_POP(folderview->mainwin);
2240 if (olditem->folder->klass->item_closed)
2241 olditem->folder->klass->item_closed(olditem);
2246 if (folderview->opened &&
2247 !GTK_CMCTREE_ROW(folderview->opened)->children)
2248 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2250 folderview->opened = NULL;
2252 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2253 gint column, FolderView *folderview)
2255 static gboolean can_select = TRUE; /* exclusive lock */
2260 GtkCMCTreeNode *old_opened = folderview->opened;
2262 folderview->selected = row;
2264 debug_print("newly selected %p, opened %p\n", folderview->selected,
2265 folderview->opened);
2266 if (folderview->opened == row) {
2267 folderview->open_folder = FALSE;
2272 item = gtk_cmctree_node_get_row_data(ctree, row);
2275 folderview->open_folder = FALSE;
2279 if (!can_select || summary_is_locked(folderview->summaryview)) {
2280 if (folderview->opened) {
2281 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2282 gtk_cmctree_select(ctree, folderview->opened);
2284 folderview->open_folder = FALSE;
2289 if (!folderview->open_folder) {
2296 /* Save cache for old folder */
2297 /* We don't want to lose all caches if sylpheed crashed */
2298 /* resets folderview->opened to NULL */
2299 folderview_close_opened(folderview, FALSE);
2301 /* CLAWS: set compose button type: news folder items
2302 * always have a news folder as parent */
2304 toolbar_set_compose_button
2305 (folderview->mainwin->toolbar,
2306 FOLDER_TYPE(item->folder) == F_NEWS ?
2307 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2310 debug_print("Folder %s is selected\n", item->path);
2312 if (!GTK_CMCTREE_ROW(row)->children)
2313 gtk_cmctree_expand(ctree, row);
2315 /* ungrab the mouse event */
2316 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2317 gtk_grab_remove(GTK_WIDGET(ctree));
2318 if (gdk_pointer_is_grabbed())
2319 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2323 /* TODO: wwp: avoid displaying (null) in the status bar */
2324 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2325 item->path : "(null)");
2326 debug_print("%s\n", buf);
2327 STATUSBAR_PUSH(folderview->mainwin, buf);
2330 main_window_cursor_wait(folderview->mainwin);
2332 if (folderview->scanning_folder == item->folder) {
2335 res = folder_item_open(item);
2338 if (res == -1 && item->no_select == FALSE) {
2339 main_window_cursor_normal(folderview->mainwin);
2340 STATUSBAR_POP(folderview->mainwin);
2342 alertpanel_error(_("Folder could not be opened."));
2344 folderview->open_folder = FALSE;
2348 } else if (res == -2 && item->no_select == FALSE) {
2349 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2350 data->ctree = ctree;
2352 data->column = column;
2353 data->folderview = folderview;
2354 debug_print("postponing open of %s till end of scan\n",
2355 item->path ? item->path:item->name);
2356 folderview->open_folder = FALSE;
2358 if (folderview->postpone_select_id != 0)
2359 g_source_remove(folderview->postpone_select_id);
2360 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2365 main_window_cursor_normal(folderview->mainwin);
2368 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2369 opened = summary_show(folderview->summaryview, item);
2371 folder_clean_cache_memory(item);
2374 gtkut_ctree_set_focus_row(ctree, old_opened);
2375 gtk_cmctree_select(ctree, old_opened);
2376 folderview->opened = old_opened;
2378 folderview->opened = row;
2379 if (gtk_cmctree_node_is_visible(ctree, row)
2380 != GTK_VISIBILITY_FULL)
2381 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2384 STATUSBAR_POP(folderview->mainwin);
2386 folderview->open_folder = FALSE;
2391 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2392 FolderView *folderview)
2396 item = gtk_cmctree_node_get_row_data(ctree, node);
2397 cm_return_if_fail(item != NULL);
2398 item->collapsed = FALSE;
2399 folderview_update_node(folderview, node);
2402 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2403 FolderView *folderview)
2407 item = gtk_cmctree_node_get_row_data(ctree, node);
2408 cm_return_if_fail(item != NULL);
2409 item->collapsed = TRUE;
2410 folderview_update_node(folderview, node);
2413 static void folderview_popup_close(GtkMenuShell *menu_shell,
2414 FolderView *folderview)
2416 if (!folderview->opened) return;
2418 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2421 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2422 FolderView *folderview)
2424 FolderColumnType type = folderview->col_state[column].type;
2426 prefs_common.folder_col_size[type] = width;
2429 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2431 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2432 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2433 GtkCMCTreeNode *node, *parent_node;
2434 gint *col_pos = folderview->col_pos;
2435 FolderItemUpdateData hookdata;
2437 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2438 if (parent_node == NULL)
2441 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2443 text[col_pos[F_COL_FOLDER]] = item->name;
2444 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2449 gtk_cmctree_expand(ctree, parent_node);
2450 gtk_cmctree_node_set_row_data(ctree, node, item);
2452 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2453 folderview_sort_folders(folderview, parent_node, item->folder);
2455 hookdata.item = item;
2456 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2457 hookdata.msg = NULL;
2458 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2460 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2463 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2465 FolderView *folderview = (FolderView *)data;
2467 GSList *mlist = NULL;
2469 FolderItem *special_trash = NULL;
2472 if (!folderview->selected) return;
2473 item = folderview_get_selected_item(folderview);
2474 cm_return_if_fail(item != NULL);
2475 cm_return_if_fail(item->folder != NULL);
2477 if (NULL != (ac = account_find_from_item(item)))
2478 special_trash = account_get_special_folder(ac, F_TRASH);
2480 if (item != item->folder->trash && item != special_trash
2481 && !folder_has_parent_of_type(item, F_TRASH)) return;
2483 if (prefs_common.ask_on_clean) {
2484 if (alertpanel(_("Empty trash"),
2485 _("Delete all messages in trash?"),
2486 GTK_STOCK_CANCEL, g_strconcat("+", _("_Empty trash"), NULL), NULL) != G_ALERTALTERNATE)
2490 mlist = folder_item_get_msg_list(item);
2492 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2493 MsgInfo * msginfo = (MsgInfo *) cur->data;
2494 if (MSG_IS_LOCKED(msginfo->flags))
2496 /* is it partially received? (partial_recv isn't cached) */
2497 if (msginfo->total_size != 0 &&
2498 msginfo->size != (off_t)msginfo->total_size)
2499 partial_mark_for_delete(msginfo);
2501 procmsg_msg_list_free(mlist);
2503 folder_item_remove_all_msg(item);
2506 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2508 FolderView *folderview = (FolderView *)data;
2510 FolderItem *special_queue = NULL;
2512 gchar *errstr = NULL;
2514 if (!folderview->selected) return;
2515 item = folderview_get_selected_item(folderview);
2516 cm_return_if_fail(item != NULL);
2517 cm_return_if_fail(item->folder != NULL);
2519 if (NULL != (ac = account_find_from_item(item)))
2520 special_queue = account_get_special_folder(ac, F_QUEUE);
2522 if (item != item->folder->queue && item != special_queue
2523 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2525 if (procmsg_queue_is_empty(item))
2528 if (prefs_common.work_offline)
2529 if (alertpanel(_("Offline warning"),
2530 _("You're working offline. Override?"),
2531 GTK_STOCK_NO, GTK_STOCK_YES,
2532 NULL) != G_ALERTALTERNATE)
2535 /* ask for confirmation before sending queued messages only
2536 in online mode and if there is at least one message queued
2537 in any of the folder queue
2539 if (prefs_common.confirm_send_queued_messages) {
2540 if (!prefs_common.work_offline) {
2541 if (alertpanel(_("Send queued messages"),
2542 _("Send all queued messages?"),
2543 GTK_STOCK_CANCEL, _("_Send"),
2544 NULL) != G_ALERTALTERNATE)
2549 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2551 alertpanel_error_log(_("Some errors occurred while "
2552 "sending queued messages."));
2554 alertpanel_error_log(_("Some errors occurred "
2555 "while sending queued messages:\n%s"), errstr);
2561 static void folderview_search_cb(GtkAction *action, gpointer data)
2563 FolderView *folderview = (FolderView *)data;
2564 summary_search(folderview->summaryview);
2567 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2569 FolderView *folderview = (FolderView *)data;
2572 if (!folderview->selected) return;
2574 item = folderview_get_selected_item(folderview);
2575 cm_return_if_fail(item != NULL);
2576 cm_return_if_fail(item->folder != NULL);
2578 item->processing_pending = TRUE;
2579 folder_item_apply_processing(item);
2580 item->processing_pending = FALSE;
2583 static void folderview_property_cb(GtkAction *action, gpointer data)
2585 FolderView *folderview = (FolderView *)data;
2588 if (!folderview->selected) return;
2590 item = folderview_get_selected_item(folderview);
2591 cm_return_if_fail(item != NULL);
2592 cm_return_if_fail(item->folder != NULL);
2594 prefs_folder_item_open(item);
2597 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2599 GSList *list = NULL;
2600 GSList *done = NULL;
2601 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2603 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2604 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2605 && list->data != node) {
2606 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2607 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2610 for (list = done; list != NULL; list = g_slist_next(list)) {
2611 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2617 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2618 FolderItem *to_folder, gboolean copy)
2620 FolderItem *new_folder = NULL;
2624 cm_return_if_fail(folderview != NULL);
2625 cm_return_if_fail(from_folder != NULL);
2626 cm_return_if_fail(to_folder != NULL);
2628 if (prefs_common.warn_dnd) {
2629 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2630 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2631 from_folder->name, to_folder->name);
2632 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2633 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2634 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2637 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2639 else if (status & G_ALERTDISABLE)
2640 prefs_common.warn_dnd = FALSE;
2643 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2644 from_folder->name, to_folder->name);
2645 STATUSBAR_PUSH(folderview->mainwin, buf);
2647 summary_clear_all(folderview->summaryview);
2648 folderview->opened = NULL;
2649 folderview->selected = NULL;
2650 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2652 main_window_cursor_wait(folderview->mainwin);
2654 statusbar_verbosity_set(FALSE);
2655 folder_item_update_freeze();
2656 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2657 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2658 statusbar_verbosity_set(FALSE);
2659 main_window_cursor_normal(folderview->mainwin);
2660 STATUSBAR_POP(folderview->mainwin);
2661 folder_item_update_thaw();
2662 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2664 folderview_sort_folders(folderview,
2665 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2666 NULL, to_folder), new_folder->folder);
2667 folderview_select(folderview, new_folder);
2668 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2670 statusbar_verbosity_set(FALSE);
2671 main_window_cursor_normal(folderview->mainwin);
2672 STATUSBAR_POP(folderview->mainwin);
2673 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2674 folder_item_update_thaw();
2676 case F_MOVE_FAILED_DEST_IS_PARENT:
2677 alertpanel_error(_("Source and destination are the same."));
2679 case F_MOVE_FAILED_DEST_IS_CHILD:
2680 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2681 _("Can't move a folder to one of its children."));
2683 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2684 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2687 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2692 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2695 static gint folderview_clist_compare(GtkCMCList *clist,
2696 gconstpointer ptr1, gconstpointer ptr2)
2698 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2699 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2701 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2703 return item1->order - item2->order;
2706 // if only one folder has an order it comes first
2707 if (item1->order > 0)
2711 if (item2->order > 0)
2717 return (item2->name != NULL);
2721 return g_utf8_collate(item1->name, item2->name);
2724 static void folderview_processing_cb(GtkAction *action, gpointer data)
2726 FolderView *folderview = (FolderView *)data;
2730 if (!folderview->selected) return;
2732 item = folderview_get_selected_item(folderview);
2733 cm_return_if_fail(item != NULL);
2734 cm_return_if_fail(item->folder != NULL);
2736 id = folder_item_get_identifier(item);
2737 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2740 prefs_filtering_open(&item->prefs->processing, title,
2741 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2745 void folderview_set_target_folder_color(gint color_op)
2749 FolderView *folderview;
2751 for (list = folderview_list; list != NULL; list = list->next) {
2752 folderview = (FolderView *)list->data;
2753 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2755 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2756 folderview->color_op;
2762 static gchar *last_smallfont = NULL;
2763 static gchar *last_normalfont = NULL;
2764 static gchar *last_boldfont = NULL;
2765 static gboolean last_derive = 0;
2767 void folderview_reinit_fonts(FolderView *folderview)
2770 g_free(last_smallfont);
2771 last_smallfont = NULL;
2772 g_free(last_normalfont);
2773 last_normalfont = NULL;
2774 g_free(last_boldfont);
2775 last_boldfont = NULL;
2778 void folderview_reflect_prefs(void)
2780 gboolean update_font = FALSE;
2781 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2782 FolderItem *item = folderview_get_selected_item(folderview);
2783 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2784 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2785 gint height = gtk_adjustment_get_value(pos);
2787 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2788 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2789 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2790 last_derive != prefs_common.derive_from_normal_font)
2796 g_free(last_smallfont);
2797 last_smallfont = g_strdup(SMALL_FONT);
2798 g_free(last_normalfont);
2799 last_normalfont = g_strdup(NORMAL_FONT);
2800 g_free(last_boldfont);
2801 last_boldfont = g_strdup(BOLD_FONT);
2802 last_derive = prefs_common.derive_from_normal_font;
2804 #define STYLE_FREE(s) \
2806 g_object_unref(s); \
2810 STYLE_FREE(normal_style);
2811 STYLE_FREE(normal_color_style);
2812 STYLE_FREE(bold_style);
2813 STYLE_FREE(bold_color_style);
2814 STYLE_FREE(bold_tgtfold_style);
2818 folderview_init(folderview);
2819 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2820 folderview_column_set_titles(folderview);
2821 folderview_set_all();
2823 g_signal_handlers_block_by_func
2824 (G_OBJECT(folderview->ctree),
2825 G_CALLBACK(folderview_selected), folderview);
2828 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2829 GTK_CMCTREE(folderview->ctree), NULL, item);
2831 folderview_select(folderview, item);
2832 folderview->open_folder = FALSE;
2833 folderview->selected = node;
2836 g_signal_handlers_unblock_by_func
2837 (G_OBJECT(folderview->ctree),
2838 G_CALLBACK(folderview_selected), folderview);
2840 pos = gtk_scrolled_window_get_vadjustment(
2841 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2842 gtk_adjustment_set_value(pos, height);
2843 gtk_adjustment_changed(pos);
2844 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2847 static void drag_state_stop(FolderView *folderview)
2849 if (folderview->drag_timer_id)
2850 g_source_remove(folderview->drag_timer_id);
2851 folderview->drag_timer_id = 0;
2852 folderview->drag_node = NULL;
2855 static gboolean folderview_defer_expand(FolderView *folderview)
2857 if (folderview->drag_node) {
2858 folderview_recollapse_nodes(folderview, folderview->drag_node);
2859 if (folderview->drag_item->collapsed) {
2860 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2861 folderview->nodes_to_recollapse = g_slist_append
2862 (folderview->nodes_to_recollapse, folderview->drag_node);
2865 folderview->drag_item = NULL;
2866 folderview->drag_timer_id = 0;
2870 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2872 /* the idea is that we call drag_state_start() whenever we want expansion to
2873 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2874 * we need to call drag_state_stop() */
2875 drag_state_stop(folderview);
2876 /* request expansion */
2877 if (0 != (folderview->drag_timer_id = g_timeout_add
2878 (prefs_common.hover_timeout,
2879 (GSourceFunc)folderview_defer_expand,
2881 folderview->drag_node = node;
2882 folderview->drag_item = item;
2885 #ifndef GENERIC_UMPC
2886 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2887 FolderView *folderview)
2889 GdkDragContext *context;
2891 cm_return_if_fail(folderview != NULL);
2892 if (folderview->selected == NULL) return;
2893 if (folderview->nodes_to_recollapse)
2894 g_slist_free(folderview->nodes_to_recollapse);
2895 folderview->nodes_to_recollapse = NULL;
2896 context = gtk_drag_begin(widget, folderview->target_list,
2897 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2898 gtk_drag_set_icon_default(context);
2901 static void folderview_drag_data_get(GtkWidget *widget,
2902 GdkDragContext *drag_context,
2903 GtkSelectionData *selection_data,
2906 FolderView *folderview)
2910 gchar *source = NULL;
2911 if (info == TARGET_DUMMY) {
2912 sel = GTK_CMCLIST(folderview->ctree)->selection;
2916 item = gtk_cmctree_node_get_row_data
2917 (GTK_CMCTREE(folderview->ctree),
2918 GTK_CMCTREE_NODE(sel->data));
2920 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2921 gtk_selection_data_set(selection_data,
2922 gtk_selection_data_get_target(selection_data), 8,
2923 source, strlen(source));
2926 g_warning("unknown info %d", info);
2930 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2932 FolderUpdateData *hookdata;
2933 FolderView *folderview;
2937 folderview = (FolderView *) userdata;
2938 cm_return_val_if_fail(hookdata != NULL, FALSE);
2939 cm_return_val_if_fail(folderview != NULL, FALSE);
2941 ctree = folderview->ctree;
2942 cm_return_val_if_fail(ctree != NULL, FALSE);
2944 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2945 folderview_create_folder_node(folderview, hookdata->item);
2946 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2947 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2948 NULL, folder_item_parent(hookdata->item));
2949 folderview_sort_folders(folderview, node, hookdata->folder);
2950 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2951 GtkCMCTreeNode *node;
2953 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2955 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2956 if (folderview->selected == node)
2957 folderview->selected = NULL;
2958 if (folderview->opened == node)
2959 folderview->opened = NULL;
2961 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2962 /* do nothing, it's done by the ADD and REMOVE) */
2963 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2964 folderview_set(folderview);
2969 static gboolean folderview_dnd_scroll_cb(gpointer data)
2971 FolderView *folderview = (FolderView *)data;
2972 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2973 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2974 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2975 gint max = (int)gtk_adjustment_get_upper(pos) -
2976 (int)gtk_adjustment_get_page_size(pos);
2978 if (folderview->scroll_value == 0) {
2979 folderview->scroll_timeout_id = 0;
2983 if (folderview->scroll_value > 0 && new_val > max) {
2985 } else if (folderview->scroll_value < 0 && new_val < 0) {
2988 gtk_adjustment_set_value(pos, new_val);
2993 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2994 GdkDragContext *context,
2998 FolderView *folderview)
3001 FolderItem *item = NULL, *src_item = NULL;
3002 GtkCMCTreeNode *node = NULL;
3003 gboolean acceptable = FALSE;
3004 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
3005 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
3006 int height = (int)gtk_adjustment_get_page_size(pos);
3007 int total_height = (int)gtk_adjustment_get_upper(pos);
3008 int vpos = (int)gtk_adjustment_get_value(pos);
3009 int offset = prefs_common.show_col_headers ? 24:0;
3012 if (gtk_cmclist_get_selection_info
3013 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
3014 GtkWidget *srcwidget;
3016 if (y > height - (48 - offset) && height + vpos < total_height) {
3017 dist = -(height - (48 - offset) - y);
3018 folderview->scroll_value = 1.41f * (1+(dist / 6));
3019 } else if (y < 72 - (24 - offset) && y >= 0) {
3020 dist = 72 - (24 - offset) - y;
3021 folderview->scroll_value = -1.41f * (1+(dist / 6));
3023 folderview->scroll_value = 0;
3025 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
3026 folderview->scroll_timeout_id =
3027 g_timeout_add(30, folderview_dnd_scroll_cb,
3031 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3032 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3033 src_item = folderview->summaryview->folder_item;
3035 srcwidget = gtk_drag_get_source_widget(context);
3036 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
3037 /* comes from summaryview */
3038 /* we are copying messages, so only accept folder items that are not
3039 the source item, are no root items and can copy messages */
3040 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
3041 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3042 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3044 } else if (srcwidget == folderview->ctree) {
3045 /* comes from folderview */
3046 /* we are moving folder items, only accept folders that are not
3047 the source items and can copy messages and create folder items */
3048 if (item && item->folder && src_item && src_item != item &&
3049 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
3050 FOLDER_CLASS(item->folder)->create_folder != NULL &&
3051 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
3052 || item->folder == src_item->folder))
3055 /* comes from another app */
3056 /* we are adding messages, so only accept folder items that are
3057 no root items and can copy messages */
3058 if (item && item->folder && folder_item_parent(item) != NULL
3059 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
3060 FOLDER_TYPE(item->folder) != F_UNKNOWN)
3065 if (acceptable || (src_item && src_item == item))
3066 drag_state_start(folderview, node, item);
3069 g_signal_handlers_block_by_func
3071 G_CALLBACK(folderview_selected), folderview);
3072 gtk_cmctree_select(GTK_CMCTREE(widget), node);
3073 g_signal_handlers_unblock_by_func
3075 G_CALLBACK(folderview_selected), folderview);
3076 gdk_drag_status(context,
3077 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
3078 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
3080 if (folderview->opened)
3081 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3082 gdk_drag_status(context, 0, time);
3088 static void folderview_drag_leave_cb(GtkWidget *widget,
3089 GdkDragContext *context,
3091 FolderView *folderview)
3093 drag_state_stop(folderview);
3094 folderview->scroll_value = 0;
3095 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
3098 static void free_info (gpointer stuff, gpointer data)
3103 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
3104 guint time, FolderItem *item)
3107 GSList *msglist = NULL;
3108 list = uri_list_extract_filenames(data);
3109 if (!(item && item->folder && folder_item_parent(item) != NULL
3110 && FOLDER_CLASS(item->folder)->add_msg != NULL))
3112 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3113 debug_print("item doesn't fit\n");
3117 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3118 debug_print("list is empty\n");
3121 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3122 MsgFileInfo *info = NULL;
3124 if (file_is_email((gchar *)tmp->data)) {
3125 info = g_new0(MsgFileInfo, 1);
3126 info->msginfo = NULL;
3127 info->file = (gchar *)tmp->data;
3128 msglist = g_slist_prepend(msglist, info);
3129 debug_print("file is a mail\n");
3131 debug_print("file isn't a mail\n");
3135 msglist = g_slist_reverse(msglist);
3136 folder_item_add_msgs(item, msglist, FALSE);
3137 g_slist_foreach(msglist, free_info, NULL);
3138 g_slist_free(msglist);
3139 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3141 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3143 list_free_strings(list);
3147 static void folderview_drag_received_cb(GtkWidget *widget,
3148 GdkDragContext *drag_context,
3151 GtkSelectionData *data,
3154 FolderView *folderview)
3157 FolderItem *item = NULL, *src_item;
3158 GtkCMCTreeNode *node;
3159 int offset = prefs_common.show_col_headers ? 24:0;
3161 folderview->scroll_value = 0;
3163 if (info == TARGET_DUMMY) {
3164 drag_state_stop(folderview);
3165 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3166 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3167 /* comes from summaryview */
3168 if (gtk_cmclist_get_selection_info
3169 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3172 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3173 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3174 src_item = folderview->summaryview->folder_item;
3176 if (item->no_select) {
3177 alertpanel_error(_("The destination folder can only be used to "
3178 "store subfolders."));
3181 /* re-check (due to acceptable possibly set for folder moves */
3182 if (!(item && item->folder && item->path && !item->no_select &&
3183 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3186 if (item && src_item) {
3187 switch (gdk_drag_context_get_selected_action(drag_context)) {
3188 case GDK_ACTION_COPY:
3189 summary_copy_selected_to(folderview->summaryview, item);
3190 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3192 case GDK_ACTION_MOVE:
3193 case GDK_ACTION_DEFAULT:
3195 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3196 summary_copy_selected_to(folderview->summaryview, item);
3198 summary_move_selected_to(folderview->summaryview, item);
3199 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3202 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3204 /* comes from folderview */
3206 gboolean folder_is_normal = TRUE;
3207 gboolean copy = (GDK_ACTION_COPY ==
3208 gdk_drag_context_get_selected_action(drag_context));
3210 source = (char *)gtk_selection_data_get_data(data) + 17;
3211 if (gtk_cmclist_get_selection_info
3212 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3214 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3217 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3218 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3219 src_item = folder_find_item_from_identifier(source);
3223 src_item->stype == F_NORMAL &&
3224 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3225 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3226 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3227 !folder_has_parent_of_type(src_item, F_TRASH);
3228 if (!item || !src_item || !folder_is_normal) {
3229 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3233 folderview_move_folder(folderview, src_item, item, copy);
3234 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3236 folderview->nodes_to_recollapse = NULL;
3237 } else if (info == TARGET_MAIL_URI_LIST) {
3238 if (gtk_cmclist_get_selection_info
3239 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3242 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3244 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3245 debug_print("no node\n");
3248 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3250 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3251 debug_print("no item\n");
3254 folderview_finish_dnd(gtk_selection_data_get_data(data),
3255 drag_context, time, item);
3259 static void folderview_drag_end_cb(GtkWidget *widget,
3260 GdkDragContext *drag_context,
3261 FolderView *folderview)
3263 drag_state_stop(folderview);
3264 folderview->scroll_value = 0;
3265 g_slist_free(folderview->nodes_to_recollapse);
3266 folderview->nodes_to_recollapse = NULL;
3269 void folderview_register_popup(FolderViewPopup *fpopup)
3273 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3274 FolderView *folderview = folderviews->data;
3275 GtkActionGroup *factory;
3277 factory = create_action_group(folderview, fpopup);
3278 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3280 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3283 void folderview_unregister_popup(FolderViewPopup *fpopup)
3288 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3289 FolderView *folderview = folderviews->data;
3291 g_hash_table_remove(folderview->popups, fpopup->klass);
3293 g_hash_table_remove(folderview_popups, fpopup->klass);
3296 void folderview_remove_item(FolderView *folderview, FolderItem *item)
3298 g_return_if_fail(folderview != NULL);
3299 g_return_if_fail(item != NULL);
3301 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
3302 g_return_if_fail(ctree != NULL);
3304 GtkCMCTreeNode *node =
3305 gtk_cmctree_find_by_row_data(ctree, NULL, item);
3306 g_return_if_fail(node != NULL);
3308 gtk_cmctree_remove_node(ctree, node);
3311 void folderview_freeze(FolderView *folderview)
3314 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
3317 void folderview_thaw(FolderView *folderview)
3320 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
3323 void folderview_grab_focus(FolderView *folderview)
3326 gtk_widget_grab_focus(folderview->ctree);