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"
62 #define COL_FOLDER_WIDTH 150
63 #define COL_NUM_WIDTH 32
65 static GList *folderview_list = NULL;
67 static GtkStyle *normal_style;
68 static GtkStyle *normal_color_style;
69 static GtkStyle *bold_style;
70 static GtkStyle *bold_color_style;
71 static GtkStyle *bold_tgtfold_style;
73 static GdkPixbuf *inboxxpm;
74 static GdkPixbuf *inboxhrmxpm;
75 static GdkPixbuf *inboxopenxpm;
76 static GdkPixbuf *inboxopenhrmxpm;
77 static GdkPixbuf *outboxxpm;
78 static GdkPixbuf *outboxhrmxpm;
79 static GdkPixbuf *outboxopenxpm;
80 static GdkPixbuf *outboxopenhrmxpm;
81 static GdkPixbuf *folderxpm;
82 static GdkPixbuf *folderhrmxpm;
83 static GdkPixbuf *folderopenxpm;
84 static GdkPixbuf *folderopenhrmxpm;
85 static GdkPixbuf *trashopenxpm;
86 static GdkPixbuf *trashopenhrmxpm;
87 static GdkPixbuf *trashxpm;
88 static GdkPixbuf *trashhrmxpm;
89 static GdkPixbuf *queuexpm;
90 static GdkPixbuf *queuehrmxpm;
91 static GdkPixbuf *queueopenxpm;
92 static GdkPixbuf *queueopenhrmxpm;
93 static GdkPixbuf *draftsxpm;
94 static GdkPixbuf *draftsopenxpm;
95 static GdkPixbuf *noselectxpm;
97 static GdkPixbuf *m_inboxxpm;
98 static GdkPixbuf *m_inboxhrmxpm;
99 static GdkPixbuf *m_inboxopenxpm;
100 static GdkPixbuf *m_inboxopenhrmxpm;
101 static GdkPixbuf *m_outboxxpm;
102 static GdkPixbuf *m_outboxhrmxpm;
103 static GdkPixbuf *m_outboxopenxpm;
104 static GdkPixbuf *m_outboxopenhrmxpm;
105 static GdkPixbuf *m_folderxpm;
106 static GdkPixbuf *m_folderhrmxpm;
107 static GdkPixbuf *m_folderopenxpm;
108 static GdkPixbuf *m_folderopenhrmxpm;
109 static GdkPixbuf *m_trashopenxpm;
110 static GdkPixbuf *m_trashopenhrmxpm;
111 static GdkPixbuf *m_trashxpm;
112 static GdkPixbuf *m_trashhrmxpm;
113 static GdkPixbuf *m_queuexpm;
114 static GdkPixbuf *m_queuehrmxpm;
115 static GdkPixbuf *m_queueopenxpm;
116 static GdkPixbuf *m_queueopenhrmxpm;
117 static GdkPixbuf *m_draftsxpm;
118 static GdkPixbuf *m_draftsopenxpm;
120 static GdkPixbuf *newxpm;
121 static GdkPixbuf *unreadxpm;
122 static GdkPixbuf *readxpm;
124 static void folderview_select_node (FolderView *folderview,
125 GtkCMCTreeNode *node);
126 static void folderview_set_folders (FolderView *folderview);
127 static void folderview_sort_folders (FolderView *folderview,
128 GtkCMCTreeNode *root,
130 static void folderview_append_folder (FolderView *folderview,
132 static void folderview_update_node (FolderView *folderview,
133 GtkCMCTreeNode *node);
135 static gint folderview_clist_compare (GtkCMCList *clist,
139 /* callback functions */
140 static gboolean folderview_button_pressed (GtkWidget *ctree,
141 GdkEventButton *event,
142 FolderView *folderview);
143 static gboolean folderview_button_released (GtkWidget *ctree,
144 GdkEventButton *event,
145 FolderView *folderview);
146 static gboolean folderview_key_pressed (GtkWidget *widget,
148 FolderView *folderview);
149 static void folderview_selected (GtkCMCTree *ctree,
152 FolderView *folderview);
153 static void folderview_tree_expanded (GtkCMCTree *ctree,
154 GtkCMCTreeNode *node,
155 FolderView *folderview);
156 static void folderview_tree_collapsed (GtkCMCTree *ctree,
157 GtkCMCTreeNode *node,
158 FolderView *folderview);
159 static void folderview_popup_close (GtkMenuShell *menu_shell,
160 FolderView *folderview);
161 static void folderview_col_resized (GtkCMCList *clist,
164 FolderView *folderview);
166 static void mark_all_read_cb (GtkAction *action,
169 static void folderview_empty_trash_cb (GtkAction *action,
172 static void folderview_send_queue_cb (GtkAction *action,
175 static void folderview_search_cb (GtkAction *action,
177 static void folderview_run_processing_cb(GtkAction *action,
180 static void folderview_property_cb (GtkAction *action,
183 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
184 GdkDragContext *context,
188 FolderView *folderview);
189 static void folderview_drag_leave_cb (GtkWidget *widget,
190 GdkDragContext *context,
192 FolderView *folderview);
193 static void folderview_drag_received_cb (GtkWidget *widget,
194 GdkDragContext *drag_context,
197 GtkSelectionData *data,
200 FolderView *folderview);
202 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
203 FolderView *folderview);
205 static void folderview_drag_data_get (GtkWidget *widget,
206 GdkDragContext *drag_context,
207 GtkSelectionData *selection_data,
210 FolderView *folderview);
211 static void folderview_drag_end_cb (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 FolderView *folderview);
215 static void folderview_create_folder_node (FolderView *folderview,
217 static gboolean folderview_update_folder (gpointer source,
219 static gboolean folderview_update_item_claws (gpointer source,
221 static void folderview_processing_cb(GtkAction *action, gpointer data);
222 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
223 GdkEventButton *event);
225 GHashTable *folderview_popups;
227 static GtkActionEntry folderview_common_popup_entries[] =
229 {"FolderViewPopup", NULL, "FolderViewPopup" },
230 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
231 {"FolderViewPopup/---", NULL, "---" },
232 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
233 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
234 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
235 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
236 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
237 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
241 GtkTargetEntry folderview_drag_types[] =
243 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
244 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
247 void folderview_initialize(void)
249 FolderViewPopup *fpopup;
251 fpopup = g_new0(FolderViewPopup, 1);
253 fpopup->klass = "common";
254 fpopup->path = "<CommonFolder>";
255 fpopup->entries = folderview_common_popup_entries;
256 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
257 fpopup->set_sensitivity = NULL;
259 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
260 g_hash_table_insert(folderview_popups, "common", fpopup);
263 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
265 FolderViewPopup *fpopup_common;
266 GtkActionGroup *action_group;
268 action_group = cm_menu_create_action_group(
270 fpopup->entries, fpopup->n_entries,
271 (gpointer)folderview);
273 if (fpopup->toggle_entries)
274 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
275 fpopup->n_toggle_entries,
276 (gpointer)folderview);
277 if (fpopup->radio_entries)
278 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
279 fpopup->n_radio_entries, fpopup->radio_default,
280 G_CALLBACK(fpopup->radio_callback),
281 (gpointer)folderview);
283 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
284 if (fpopup_common != fpopup) {
285 gtk_action_group_add_actions(action_group, fpopup_common->entries,
286 fpopup_common->n_entries,
287 (gpointer)folderview);
288 if (fpopup_common->toggle_entries)
289 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
290 fpopup_common->n_toggle_entries,
291 (gpointer)folderview);
292 if (fpopup_common->radio_entries)
293 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
294 fpopup_common->n_radio_entries, fpopup_common->radio_default,
295 G_CALLBACK(fpopup_common->radio_callback),
296 (gpointer)folderview);
302 static void create_action_groups(gpointer key, gpointer value, gpointer data)
304 FolderView *folderview = data;
305 FolderViewPopup *fpopup = value;
306 GtkActionGroup *group;
308 group = create_action_group(folderview, fpopup);
309 g_hash_table_insert(folderview->popups, fpopup->klass, group);
312 static void folderview_column_set_titles(FolderView *folderview)
314 GtkWidget *ctree = folderview->ctree;
315 GtkWidget *label_folder;
316 GtkWidget *label_new;
317 GtkWidget *label_unread;
318 GtkWidget *label_total;
319 GtkWidget *hbox_folder;
321 GtkWidget *hbox_unread;
322 GtkWidget *hbox_total;
323 gint *col_pos = folderview->col_pos;
325 debug_print("setting titles...\n");
326 gtk_widget_realize(folderview->ctree);
327 gtk_widget_show_all(folderview->scrolledwin);
329 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
330 * instead text (text overflows making them unreadable and ugly) */
331 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
333 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
335 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
338 label_folder = gtk_label_new(_("Folder"));
339 label_new = gtk_image_new_from_pixbuf(newxpm);
340 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
341 label_total = gtk_image_new_from_pixbuf(readxpm);
343 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
345 hbox_folder = gtk_hbox_new(FALSE, 4);
346 hbox_new = gtk_hbox_new(FALSE, 4);
347 hbox_unread = gtk_hbox_new(FALSE, 4);
348 hbox_total = gtk_hbox_new(FALSE, 4);
351 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
352 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
353 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
354 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
355 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
356 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
357 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
358 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
360 gtk_widget_show_all(hbox_folder);
361 gtk_widget_show_all(hbox_new);
362 gtk_widget_show_all(hbox_unread);
363 gtk_widget_show_all(hbox_total);
366 gtk_widget_set_size_request(hbox_new, -1, 20);
367 gtk_widget_set_size_request(hbox_unread, -1, 20);
368 gtk_widget_set_size_request(hbox_total, -1, 20);
371 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
372 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
373 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
374 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
380 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
381 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
382 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
385 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
387 FolderView *folderview = (FolderView *)data;
388 GdkEventButton event;
389 if (folderview_get_selected_item(folderview) == NULL)
393 event.time = gtk_get_current_event_time();
395 folderview_set_sens_and_popup_menu(folderview, -1,
402 static GtkWidget *folderview_ctree_create(FolderView *folderview)
406 FolderColumnState *col_state;
407 FolderColumnType type;
408 gchar *titles[N_FOLDER_COLS];
410 GtkWidget *scrolledwin = folderview->scrolledwin;
412 debug_print("creating tree...\n");
413 memset(titles, 0, sizeof(titles));
415 col_state = prefs_folder_column_get_config();
416 memset(titles, 0, sizeof(titles));
418 col_pos = folderview->col_pos;
420 for (i = 0; i < N_FOLDER_COLS; i++) {
421 folderview->col_state[i] = col_state[i];
422 type = col_state[i].type;
426 titles[col_pos[F_COL_FOLDER]] = _("Folder");
427 titles[col_pos[F_COL_NEW]] = _("New");
428 titles[col_pos[F_COL_UNREAD]] = _("Unread");
429 /* TRANSLATORS: This in Number sign in American style */
430 titles[col_pos[F_COL_TOTAL]] = _("#");
432 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
435 if (prefs_common.show_col_headers == FALSE)
436 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
439 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
440 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
442 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
443 col_pos[F_COL_UNREAD],
445 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
446 col_pos[F_COL_TOTAL],
448 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
449 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
450 GTK_CMCTREE_EXPANDER_TRIANGLE);
452 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
453 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
455 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
456 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
458 /* don't let title buttons take key focus */
459 for (i = 0; i < N_FOLDER_COLS; i++) {
460 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
461 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
462 prefs_common.folder_col_size[i]);
463 gtk_cmclist_set_column_visibility
464 (GTK_CMCLIST(ctree), i, col_state[i].visible);
467 g_signal_connect(G_OBJECT(ctree), "key_press_event",
468 G_CALLBACK(folderview_key_pressed),
470 g_signal_connect(G_OBJECT(ctree), "button_press_event",
471 G_CALLBACK(folderview_button_pressed),
473 g_signal_connect(G_OBJECT(ctree), "popup-menu",
474 G_CALLBACK(folderview_popup_menu), folderview);
475 g_signal_connect(G_OBJECT(ctree), "button_release_event",
476 G_CALLBACK(folderview_button_released),
478 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
479 G_CALLBACK(folderview_selected), folderview);
481 /* drag-n-dropping folders on maemo is impractical as this
482 * opens the folder almost everytime */
483 g_signal_connect(G_OBJECT(ctree), "start_drag",
484 G_CALLBACK(folderview_start_drag), folderview);
486 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
487 G_CALLBACK(folderview_drag_data_get),
490 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
491 G_CALLBACK(folderview_tree_expanded),
493 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
494 G_CALLBACK(folderview_tree_collapsed),
497 g_signal_connect(G_OBJECT(ctree), "resize_column",
498 G_CALLBACK(folderview_col_resized),
502 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
503 folderview_drag_types, 2,
504 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
505 g_signal_connect(G_OBJECT(ctree), "drag_motion",
506 G_CALLBACK(folderview_drag_motion_cb),
508 g_signal_connect(G_OBJECT(ctree), "drag_leave",
509 G_CALLBACK(folderview_drag_leave_cb),
511 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
512 G_CALLBACK(folderview_drag_received_cb),
514 g_signal_connect(G_OBJECT(ctree), "drag_end",
515 G_CALLBACK(folderview_drag_end_cb),
518 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
523 void folderview_set_column_order(FolderView *folderview)
525 GtkWidget *ctree = folderview->ctree;
526 FolderItem *item = folderview_get_selected_item(folderview);
527 FolderItem *sel_item = NULL, *op_item = NULL;
528 GtkWidget *scrolledwin = folderview->scrolledwin;
530 if (folderview->drag_timer_id != 0) {
531 g_source_remove(folderview->drag_timer_id);
532 folderview->drag_timer_id = 0;
534 if (folderview->deferred_refresh_id != 0) {
535 g_source_remove(folderview->deferred_refresh_id);
536 folderview->deferred_refresh_id = 0;
538 if (folderview->scroll_timeout_id != 0) {
539 g_source_remove(folderview->scroll_timeout_id);
540 folderview->scroll_timeout_id = 0;
542 if (folderview->postpone_select_id != 0) {
543 g_source_remove(folderview->postpone_select_id);
544 folderview->postpone_select_id = 0;
547 if (folderview->selected)
548 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
549 if (folderview->opened)
550 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
552 debug_print("recreating tree...\n");
553 gtk_widget_destroy(folderview->ctree);
556 folderview->ctree = ctree = folderview_ctree_create(folderview);
557 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
558 GTK_CMCLIST(ctree)->hadjustment);
559 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
560 GTK_CMCLIST(ctree)->vadjustment);
561 gtk_widget_show(ctree);
564 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
566 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
568 folderview_set(folderview);
569 folderview_column_set_titles(folderview);
571 folderview_select(folderview,item);
574 FolderView *folderview_create(void)
576 FolderView *folderview;
577 GtkWidget *scrolledwin;
580 debug_print("Creating folder view...\n");
581 folderview = g_new0(FolderView, 1);
583 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
584 gtk_scrolled_window_set_policy
585 (GTK_SCROLLED_WINDOW(scrolledwin),
586 GTK_POLICY_AUTOMATIC,
587 prefs_common.folderview_vscrollbar_policy);
588 gtk_widget_set_size_request(scrolledwin,
589 prefs_common.folderview_width,
590 prefs_common.folderview_height);
592 folderview->scrolledwin = scrolledwin;
593 ctree = folderview_ctree_create(folderview);
595 /* create popup factories */
596 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
597 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
599 folderview->ctree = ctree;
601 folderview->folder_update_callback_id =
602 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
603 folderview->folder_item_update_callback_id =
604 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
606 gtk_widget_show_all(scrolledwin);
608 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
609 folderview_list = g_list_append(folderview_list, folderview);
611 folderview->drag_timer_id = 0;
612 folderview->deferred_refresh_id = 0;
613 folderview->scroll_timeout_id = 0;
614 folderview->postpone_select_id = 0;
619 void folderview_init(FolderView *folderview)
621 GtkWidget *ctree = folderview->ctree;
623 PangoFontDescription *normal_font;
625 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
626 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
627 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
628 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
629 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
630 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
631 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
655 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
672 normal_font = pango_font_description_from_string(NORMAL_FONT);
674 gtk_widget_modify_font(ctree, normal_font);
675 pango_font_description_free(normal_font);
677 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
680 PangoFontDescription *font_desc;
681 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
682 font_desc = pango_font_description_from_string(NORMAL_FONT);
684 if (normal_style->font_desc)
685 pango_font_description_free
686 (normal_style->font_desc);
687 normal_style->font_desc = font_desc;
689 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
690 normal_color_style = gtk_style_copy(normal_style);
691 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
695 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
696 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
697 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
698 PangoFontDescription *font_desc;
699 font_desc = pango_font_description_from_string(NORMAL_FONT);
701 pango_font_description_free(bold_style->font_desc);
702 bold_style->font_desc = font_desc;
704 pango_font_description_set_weight
705 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
707 PangoFontDescription *font_desc;
708 font_desc = pango_font_description_from_string(BOLD_FONT);
710 if (bold_style->font_desc)
711 pango_font_description_free
712 (bold_style->font_desc);
713 bold_style->font_desc = font_desc;
716 bold_color_style = gtk_style_copy(bold_style);
717 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
719 bold_tgtfold_style = gtk_style_copy(bold_style);
720 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
724 static gboolean folderview_defer_set(gpointer data)
726 FolderView *folderview = (FolderView *)data;
727 MainWindow *mainwin = folderview->mainwin;
731 if (mainwin->lock_count)
734 debug_print("doing deferred folderview_set now\n");
735 folderview_set(folderview);
737 folderview->deferred_refresh_id = 0;
741 void folderview_set(FolderView *folderview)
743 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
744 MainWindow *mainwin = folderview->mainwin;
745 FolderItem *sel_item = NULL, *op_item = NULL;
750 if (mainwin->lock_count) {
751 if (folderview->deferred_refresh_id == 0)
752 folderview->deferred_refresh_id =
753 g_timeout_add(500, folderview_defer_set, folderview);
754 debug_print("deferred folderview_set\n");
759 debug_print("Setting folder info...\n");
760 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
762 main_window_cursor_wait(mainwin);
764 if (folderview->selected)
765 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
766 if (folderview->opened)
767 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
769 folderview->selected = NULL;
770 folderview->opened = NULL;
772 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
773 gtk_cmclist_clear(GTK_CMCLIST(ctree));
775 folderview_set_folders(folderview);
778 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
780 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
782 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
783 main_window_cursor_normal(mainwin);
784 STATUSBAR_POP(mainwin);
788 void folderview_set_all(void)
792 for (list = folderview_list; list != NULL; list = list->next)
793 folderview_set((FolderView *)list->data);
796 void folderview_select(FolderView *folderview, FolderItem *item)
798 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
799 GtkCMCTreeNode *node;
800 GtkCMCTreeNode *old_selected = folderview->selected;
804 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
805 if (node) folderview_select_node(folderview, node);
807 if (old_selected != node)
808 folder_update_op_count();
811 static void mark_all_read_cb(GtkAction *action, gpointer data)
813 FolderView *folderview = (FolderView *)data;
817 item = folderview_get_selected_item(folderview);
821 if (folderview->summaryview->folder_item != item
822 && prefs_common.ask_mark_all_read) {
823 val = alertpanel_full(_("Mark all as read"),
824 _("Do you really want to mark all mails in this "
825 "folder as read?"), GTK_STOCK_NO, GTK_STOCK_YES, NULL,
826 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
828 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
830 else if (val & G_ALERTDISABLE)
831 prefs_common.ask_mark_all_read = FALSE;
835 folder_item_update_freeze();
836 if (folderview->summaryview->folder_item != item)
837 summary_lock(folderview->summaryview);
839 summary_freeze(folderview->summaryview);
841 folderutils_mark_all_read(item);
843 if (folderview->summaryview->folder_item != item)
844 summary_unlock(folderview->summaryview);
846 summary_thaw(folderview->summaryview);
847 folder_item_update_thaw();
850 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
852 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
854 cm_return_if_fail(node != NULL);
856 if (folderview->open_folder) {
860 gtkut_ctree_expand_parent_all(ctree, node);
862 folderview->open_folder = TRUE;
863 gtkut_ctree_set_focus_row(ctree, node);
864 gtk_cmctree_select(ctree, node);
865 if ((folderview->summaryview->folder_item &&
866 folderview->summaryview->folder_item->total_msgs > 0) ||
867 prefs_common.layout_mode == SMALL_LAYOUT)
868 summary_grab_focus(folderview->summaryview);
870 gtk_widget_grab_focus(folderview->ctree);
873 void folderview_unselect(FolderView *folderview)
875 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
877 (GTK_CMCTREE(folderview->ctree), folderview->opened);
879 folderview->selected = folderview->opened = NULL;
882 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
883 GtkCMCTreeNode *node,
889 node = gtkut_ctree_node_next(ctree, node);
891 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
893 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
894 item = gtk_cmctree_node_get_row_data(ctree, node);
897 if (item->stype == F_TRASH || item->stype == F_DRAFT)
901 if(item->unread_msgs > 0)
905 if(item->new_msgs > 0)
909 if(item->marked_msgs > 0)
918 void folderview_select_next_with_flag(FolderView *folderview,
922 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
923 GtkCMCTreeNode *node = NULL;
924 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
925 gboolean last_open = prefs_common.always_show_msg;
929 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
932 prefs_common.summary_select_prio[0] = ACTION_NEW;
935 prefs_common.summary_select_prio[0] = ACTION_MARKED;
938 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
940 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
942 folderview_select_node(folderview, node);
946 if (!folderview->opened ||
947 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
951 /* search again from the first node */
952 node = folderview_find_next_with_flag(ctree, NULL, flag);
954 folderview_select_node(folderview, node);
957 prefs_common.summary_select_prio[0] = last_summary_select_prio;
958 prefs_common.always_show_msg = last_open;
961 FolderItem *folderview_get_selected_item(FolderView *folderview)
963 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
965 if (!folderview->selected) return NULL;
966 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
969 static void folderview_set_folders(FolderView *folderview)
972 list = folder_get_list();
974 for (; list != NULL; list = list->next) {
975 folderview_append_folder(folderview, FOLDER(list->data));
979 static gchar *get_scan_str(FolderItem *item)
982 return g_strdup_printf(_("Scanning folder %s%c%s..."),
983 item->folder->name, G_DIR_SEPARATOR,
986 return g_strdup_printf(_("Scanning folder %s..."),
989 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
993 for (list = folderview_list; list != NULL; list = list->next) {
994 FolderView *folderview = (FolderView *)list->data;
995 MainWindow *mainwin = folderview->mainwin;
996 gchar *str = get_scan_str(item);
998 STATUSBAR_PUSH(mainwin, str);
999 STATUSBAR_POP(mainwin);
1004 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1007 MainWindow *mainwin = mainwindow_get_mainwindow();
1008 FolderView *folderview = NULL;
1009 GtkAdjustment *pos = NULL;
1012 cm_return_if_fail(folder != NULL);
1014 if (!folder->klass->scan_tree) return;
1017 alertpanel_full(_("Rebuild folder tree"),
1018 _("Rebuilding the folder tree will remove "
1019 "local caches. Do you want to continue?"),
1020 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1021 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1022 != G_ALERTALTERNATE) {
1028 window = label_window_create(_("Rebuilding folder tree..."));
1030 window = label_window_create(_("Scanning folder tree..."));
1033 folderview = mainwin->folderview;
1036 pos = gtk_scrolled_window_get_vadjustment(
1037 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1038 height = gtk_adjustment_get_value(pos);
1041 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1042 folder_scan_tree(folder, rebuild);
1043 folder_set_ui_func(folder, NULL, NULL);
1045 folderview_set_all();
1048 pos = gtk_scrolled_window_get_vadjustment(
1049 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1050 gtk_adjustment_set_value(pos, height);
1051 gtk_adjustment_changed(pos);
1053 label_window_destroy(window);
1057 /** folderview_check_new()
1058 * Scan and update the folder and return the
1059 * count the number of new messages since last check.
1060 * \param folder the folder to check for new messages
1061 * \return the number of new messages since last check
1063 gint folderview_check_new(Folder *folder)
1067 FolderView *folderview;
1069 GtkCMCTreeNode *node;
1071 gint former_new_msgs = 0;
1072 gint former_new = 0, former_unread = 0, former_total;
1074 for (list = folderview_list; list != NULL; list = list->next) {
1075 folderview = (FolderView *)list->data;
1076 ctree = GTK_CMCTREE(folderview->ctree);
1077 folderview->scanning_folder = folder;
1079 main_window_lock(folderview->mainwin);
1081 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1082 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1084 item = gtk_cmctree_node_get_row_data(ctree, node);
1085 if (!item || !item->path || !item->folder) continue;
1086 if (item->no_select) continue;
1087 if (folder && folder != item->folder) continue;
1088 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1089 if (!item->prefs->newmailcheck) continue;
1090 if (item->processing_pending == TRUE) {
1091 debug_print("skipping %s, processing pending\n",
1092 item->path ? item->path : item->name);
1095 if (item->scanning != ITEM_NOT_SCANNING) {
1096 debug_print("skipping %s, scanning\n",
1097 item->path ? item->path : item->name);
1101 str = get_scan_str(item);
1103 STATUSBAR_PUSH(folderview->mainwin, str);
1107 folderview_scan_tree_func(item->folder, item, NULL);
1108 former_new = item->new_msgs;
1109 former_unread = item->unread_msgs;
1110 former_total = item->total_msgs;
1112 if (item->folder->klass->scan_required &&
1113 (item->folder->klass->scan_required(item->folder, item) ||
1114 item->folder->inbox == item ||
1115 item->opened == TRUE ||
1116 item->processing_pending == TRUE)) {
1117 if (folder_item_scan(item) < 0) {
1119 summaryview_unlock(folderview->summaryview, item);
1120 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1121 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1122 item->path ? item->path:item->name);
1123 STATUSBAR_POP(folderview->mainwin);
1125 } else if (!FOLDER_IS_LOCAL(folder)) {
1126 STATUSBAR_POP(folderview->mainwin);
1131 } else if (!item->folder->klass->scan_required) {
1132 if (folder_item_scan(item) < 0) {
1133 summaryview_unlock(folderview->summaryview, item);
1134 if (folder && !FOLDER_IS_LOCAL(folder)) {
1135 STATUSBAR_POP(folderview->mainwin);
1140 if (former_new != item->new_msgs ||
1141 former_unread != item->unread_msgs ||
1142 former_total != item->total_msgs)
1143 folderview_update_node(folderview, node);
1145 new_msgs += item->new_msgs;
1146 former_new_msgs += former_new;
1147 STATUSBAR_POP(folderview->mainwin);
1149 folderview->scanning_folder = NULL;
1150 main_window_unlock(folderview->mainwin);
1154 folder_write_list();
1155 /* Number of new messages since last check is the just the difference
1156 * between former_new_msgs and new_msgs. If new_msgs is less than
1157 * former_new_msgs, that would mean another session accessed the folder
1158 * and the result is not well defined.
1160 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1164 void folderview_check_new_all(void)
1168 FolderView *folderview;
1170 folderview = (FolderView *)folderview_list->data;
1173 main_window_lock(folderview->mainwin);
1174 window = label_window_create
1175 (_("Checking for new messages in all folders..."));
1177 list = folder_get_list();
1178 for (; list != NULL; list = list->next) {
1179 Folder *folder = list->data;
1181 folderview_check_new(folder);
1184 folder_write_list();
1185 folderview_set_all();
1187 label_window_destroy(window);
1188 main_window_unlock(folderview->mainwin);
1192 static gboolean folderview_have_children_sub(FolderView *folderview,
1198 if (!item || !item->folder || !item->folder->node)
1201 node = item->folder->node;
1203 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1204 node = node->children;
1206 if (in_sub && item->total_msgs > 0) {
1210 while (node != NULL) {
1211 if (node && node->data) {
1212 FolderItem *next_item = (FolderItem*) node->data;
1214 if (folderview_have_children_sub(folderview,
1223 static gboolean folderview_have_children(FolderView *folderview,
1226 return folderview_have_children_sub(folderview, item, FALSE);
1229 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1235 if (!item || !item->folder || !item->folder->node)
1238 node = item->folder->node;
1240 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1241 node = node->children;
1244 (item->new_msgs > 0 ||
1245 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1249 while (node != NULL) {
1250 if (node && node->data) {
1251 FolderItem *next_item = (FolderItem*) node->data;
1253 if (folderview_have_new_children_sub(folderview,
1262 static gboolean folderview_have_new_children(FolderView *folderview,
1265 return folderview_have_new_children_sub(folderview, item, FALSE);
1268 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1274 if (!item || !item->folder || !item->folder->node)
1277 node = item->folder->node;
1279 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1280 node = node->children;
1283 (item->unread_msgs > 0 ||
1284 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1288 while (node != NULL) {
1289 if (node && node->data) {
1290 FolderItem *next_item = (FolderItem*) node->data;
1292 if (folderview_have_unread_children_sub(folderview,
1302 static gboolean folderview_have_unread_children(FolderView *folderview,
1305 return folderview_have_unread_children_sub(folderview, item, FALSE);
1308 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1314 if (!item || !item->folder || !item->folder->node)
1317 node = item->folder->node;
1319 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1320 node = node->children;
1322 if (in_sub && item->search_match){
1326 while (node != NULL) {
1327 if (node && node->data) {
1328 FolderItem *next_item = (FolderItem*) node->data;
1330 if (folderview_have_matching_children_sub(folderview,
1340 static gboolean folderview_have_matching_children(FolderView *folderview,
1343 return folderview_have_matching_children_sub(folderview, item, FALSE);
1346 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1352 if (!item || !item->folder || !item->folder->node)
1355 node = item->folder->node;
1357 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1358 node = node->children;
1360 if (item->marked_msgs != 0) {
1364 while (node != NULL) {
1365 if (node && node->data) {
1366 FolderItem *next_item = (FolderItem*) node->data;
1368 if (folderview_have_marked_children_sub(folderview,
1377 static gboolean folderview_have_marked_children(FolderView *folderview,
1380 return folderview_have_marked_children_sub(folderview, item, FALSE);
1383 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1385 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1386 GtkStyle *style = NULL;
1387 GtkStyle *color_style = NULL;
1389 GdkPixbuf *xpm, *openxpm;
1390 static GdkPixbuf *searchicon;
1391 gboolean mark = FALSE;
1394 gboolean add_unread_mark;
1395 gboolean add_sub_match_mark;
1396 gboolean use_bold, use_color;
1397 gint *col_pos = folderview->col_pos;
1398 SpecialFolderItemType stype;
1400 item = gtk_cmctree_node_get_row_data(ctree, node);
1401 cm_return_if_fail(item != NULL);
1403 if (!GTK_CMCTREE_ROW(node)->expanded)
1404 mark = folderview_have_marked_children(folderview, item);
1406 mark = (item->marked_msgs != 0);
1408 stype = item->stype;
1409 if (stype == F_NORMAL) {
1410 if (folder_has_parent_of_type(item, F_TRASH))
1412 else if (folder_has_parent_of_type(item, F_DRAFT))
1414 else if (folder_has_parent_of_type(item, F_OUTBOX))
1416 else if (folder_has_parent_of_type(item, F_QUEUE))
1421 if (item->hide_read_msgs || item->hide_read_threads) {
1422 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1423 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1425 xpm = mark?m_inboxxpm:inboxxpm;
1426 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1430 if (item->hide_read_msgs || item->hide_read_threads) {
1431 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1432 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1434 xpm = mark?m_outboxxpm:outboxxpm;
1435 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1439 if (item->hide_read_msgs || item->hide_read_threads) {
1440 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1441 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1443 xpm = mark?m_queuexpm:queuexpm;
1444 openxpm = mark?m_queueopenxpm:queueopenxpm;
1448 if (item->hide_read_msgs || item->hide_read_threads) {
1449 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1450 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1452 xpm = mark?m_trashxpm:trashxpm;
1453 openxpm = mark?m_trashopenxpm:trashopenxpm;
1457 xpm = mark?m_draftsxpm:draftsxpm;
1458 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1461 if (item->hide_read_msgs || item->hide_read_threads) {
1462 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1463 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1465 xpm = mark?m_folderxpm:folderxpm;
1466 openxpm = mark?m_folderopenxpm:folderopenxpm;
1470 if (item->no_select) {
1471 xpm = openxpm = noselectxpm;
1474 name = folder_item_get_name(item);
1476 if (!GTK_CMCTREE_ROW(node)->expanded) {
1477 add_unread_mark = folderview_have_unread_children(
1479 add_sub_match_mark = folderview_have_matching_children(
1482 add_unread_mark = FALSE;
1483 add_sub_match_mark = FALSE;
1486 if (item->search_match) {
1488 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1491 xpm = openxpm = searchicon;
1495 if (prefs_common.display_folder_unread) {
1496 if (folder_has_parent_of_type(item, F_QUEUE)) {
1497 /* only total_msgs matters here */
1498 if (item->total_msgs > 0) {
1499 /* show total number (should be equal to the unread number)
1501 str = g_strdup_printf("%s (%d%s%s)",
1502 name, item->total_msgs,
1503 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1504 (item->unreadmarked_msgs > 0) ? "!" : "");
1507 if (prefs_common.display_folder_unread == 1) {
1508 if (item->unread_msgs > 0) {
1509 /* show unread number and signs */
1510 str = g_strdup_printf("%s (%d%s%s)",
1511 name, item->unread_msgs,
1512 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1513 (item->unreadmarked_msgs > 0) ? "!" : "");
1516 if (item->total_msgs > 0) {
1517 /* show unread number, total number and signs if any */
1518 str = g_strdup_printf("%s (%d/%d%s%s)",
1519 name, item->unread_msgs, item->total_msgs,
1520 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1521 (item->unreadmarked_msgs > 0) ? "!" : "");
1525 if ((str == NULL) &&
1526 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1527 /* no unread/total numbers, but at least one sign */
1528 str = g_strdup_printf("%s (%s%s)",
1530 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1531 (item->unreadmarked_msgs > 0) ? "!" : "");
1535 /* last fallback, folder name only or with +! sign */
1536 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1537 str = g_strdup_printf("%s%s",
1539 } else if (item->unreadmarked_msgs > 0) {
1540 str = g_strdup_printf("%s%s",
1542 } else if (add_sub_match_mark) {
1543 str = g_strdup_printf("%s%s",
1546 str = g_strdup_printf("%s", name);
1549 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1551 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1555 if (!folder_item_parent(item)) {
1556 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1557 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1558 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1560 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1561 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1562 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1565 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1566 folder_has_parent_of_type(item, F_TRASH)) {
1567 use_bold = use_color = FALSE;
1568 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1569 GSList *list = folder_item_get_msg_list(item);
1571 use_bold = use_color = FALSE;
1572 for (cur = list; cur; cur = cur->next) {
1573 MsgInfo *msginfo = (MsgInfo *)cur->data;
1574 if (!MSG_IS_DELETED(msginfo->flags)) {
1575 /* highlight queue folder if there are any messages */
1576 use_bold = use_color = TRUE;
1580 if (!GTK_CMCTREE_ROW(node)->expanded &&
1581 use_bold == FALSE &&
1582 folderview_have_children(folderview, item))
1583 use_bold = use_color = TRUE;
1584 procmsg_msg_list_free(list);
1586 /* if unread messages exist, print with bold font */
1587 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1589 /* if new messages exist, print with colored letter */
1591 (item->new_msgs > 0) ||
1593 folderview_have_new_children(folderview, item));
1596 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1601 if (item->prefs->color > 0 && !use_color) {
1602 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1603 color_style = gtk_style_copy(bold_style);
1604 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1605 style = color_style;
1606 } else if (use_color) {
1607 style = bold_color_style;
1610 if (item->op_count > 0) {
1611 style = bold_tgtfold_style;
1613 } else if (use_color) {
1614 style = normal_color_style;
1615 gtk_cmctree_node_set_foreground(ctree, node,
1616 &folderview->color_new);
1617 } else if (item->op_count > 0) {
1618 style = bold_tgtfold_style;
1619 } else if (item->prefs->color > 0) {
1621 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1622 color_style = gtk_style_copy(normal_style);
1623 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1624 style = color_style;
1626 style = normal_style;
1629 gtk_cmctree_node_set_row_style(ctree, node, style);
1631 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1632 folderview_update_node(folderview, node);
1635 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1638 FolderView *folderview;
1640 GtkCMCTreeNode *node;
1642 cm_return_if_fail(item != NULL);
1644 for (list = folderview_list; list != NULL; list = list->next) {
1645 folderview = (FolderView *)list->data;
1646 ctree = GTK_CMCTREE(folderview->ctree);
1648 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1649 if (node && item->search_match != matches) {
1650 item->search_match = matches;
1651 folderview_update_node(folderview, node);
1656 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1658 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1659 FolderView *folderview = (FolderView *)data;
1661 GtkCMCTreeNode *node;
1662 cm_return_val_if_fail(update_info != NULL, TRUE);
1663 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1664 cm_return_val_if_fail(folderview != NULL, FALSE);
1666 ctree = GTK_CMCTREE(folderview->ctree);
1668 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1671 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1672 folderview_update_node(folderview, node);
1674 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1675 update_info->item == folderview->summaryview->folder_item &&
1676 update_info->item != NULL)
1677 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1678 summary_show(folderview->summaryview, update_info->item);
1684 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1685 GNode *gnode, GtkCMCTreeNode *cnode,
1688 FolderView *folderview = (FolderView *)data;
1689 FolderItem *item = FOLDER_ITEM(gnode->data);
1691 cm_return_val_if_fail(item != NULL, FALSE);
1693 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1694 folderview_update_node(folderview, cnode);
1699 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1702 FolderView *folderview = (FolderView *)data;
1705 if (GTK_CMCTREE_ROW(node)->children) {
1706 item = gtk_cmctree_node_get_row_data(ctree, node);
1707 cm_return_if_fail(item != NULL);
1709 if (!item->collapsed)
1710 gtk_cmctree_expand(ctree, node);
1712 folderview_update_node(folderview, node);
1716 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1717 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1720 GtkCMCTreeNode *node, *parent, *sibling;
1722 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1724 g_warning("%s not found.\n", item->path);
1726 parent = GTK_CMCTREE_ROW(node)->parent;
1727 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1728 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1730 sibling = GTK_CMCTREE_ROW(parent)->children;
1734 tmp = gtk_cmctree_node_get_row_data
1736 if (tmp && tmp->stype != F_NORMAL)
1737 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1741 if (node != sibling)
1742 gtk_cmctree_move(ctree, node, parent, sibling);
1749 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1752 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1753 GtkCMCTreeNode *prev = NULL;
1755 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1756 gtk_sctree_sort_recursive(ctree, root);
1757 if (root && GTK_CMCTREE_ROW(root)->parent) {
1758 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1761 set_special_folder(ctree, folder->inbox, root, &prev);
1762 set_special_folder(ctree, folder->outbox, root, &prev);
1763 set_special_folder(ctree, folder->draft, root, &prev);
1764 set_special_folder(ctree, folder->queue, root, &prev);
1765 set_special_folder(ctree, folder->trash, root, &prev);
1766 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1769 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1771 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1772 GtkCMCTreeNode *root;
1774 cm_return_if_fail(folder != NULL);
1776 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1777 folderview_gnode_func, folderview);
1778 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1780 folderview_sort_folders(folderview, root, folder);
1783 /* callback functions */
1784 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1785 GdkEventButton *event)
1789 FolderViewPopup *fpopup;
1790 GtkActionGroup *action_group;
1792 FolderItem *special_trash = NULL, *special_queue = NULL;
1794 GtkUIManager *ui_manager = gtk_ui_manager_new();
1796 if (folderview->ui_manager)
1797 g_object_unref(folderview->ui_manager);
1799 folderview->ui_manager = ui_manager;
1800 item = folderview_get_selected_item(folderview);
1802 cm_return_if_fail(item != NULL);
1803 cm_return_if_fail(item->folder != NULL);
1804 folder = item->folder;
1806 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1809 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1811 fpopup = g_hash_table_lookup(folderview_popups, "common");
1812 action_group = g_hash_table_lookup(folderview->popups, "common");
1815 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1816 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1817 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1819 if (fpopup->add_menuitems)
1820 fpopup->add_menuitems(ui_manager, item);
1822 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1823 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1824 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1825 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1826 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1827 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1829 if (fpopup->set_sensitivity != NULL)
1830 fpopup->set_sensitivity(ui_manager, item);
1832 if (NULL != (ac = account_find_from_item(item))) {
1833 special_trash = account_get_special_folder(ac, F_TRASH);
1834 special_queue = account_get_special_folder(ac, F_QUEUE);
1837 if ((item == folder->trash || item == special_trash
1838 || folder_has_parent_of_type(item, F_TRASH))) {
1839 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1840 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1843 if ((item == folder->queue || item == special_queue
1844 || folder_has_parent_of_type(item, F_QUEUE))) {
1845 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1846 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1849 #define SET_SENS(name, sens) \
1850 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1852 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1853 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1854 folderview->selected == folderview->opened);
1855 SET_SENS("FolderViewPopup/Properties", TRUE);
1857 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1858 item->total_msgs >= 1 && !item->processing_pending);
1859 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1860 !item->no_select && !item->processing_pending);
1862 if (item == folder->trash || item == special_trash
1863 || folder_has_parent_of_type(item, F_TRASH)) {
1864 GSList *msglist = folder_item_get_msg_list(item);
1865 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1866 procmsg_msg_list_free(msglist);
1868 if (item == folder->queue || item == special_queue
1869 || folder_has_parent_of_type(item, F_QUEUE)) {
1870 GSList *msglist = folder_item_get_msg_list(item);
1871 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1872 procmsg_msg_list_free(msglist);
1876 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1877 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1878 g_signal_connect(G_OBJECT(popup), "selection_done",
1879 G_CALLBACK(folderview_popup_close),
1881 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1882 event->button, event->time);
1885 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1886 FolderView *folderview)
1888 GtkCMCList *clist = GTK_CMCLIST(ctree);
1889 gint prev_row = -1, row = -1, column = -1;
1891 if (!event) return FALSE;
1893 if (event->button == 1 || event->button == 2) {
1894 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1895 folderview->open_folder = TRUE;
1897 if (event->type == GDK_2BUTTON_PRESS) {
1898 if (clist->selection) {
1899 GtkCMCTreeNode *node;
1901 node = GTK_CMCTREE_NODE(clist->selection->data);
1903 gtk_cmctree_toggle_expansion(
1906 folderview->open_folder = FALSE;
1913 if (event->button == 2 || event->button == 3) {
1915 if (clist->selection) {
1916 GtkCMCTreeNode *node;
1918 node = GTK_CMCTREE_NODE(clist->selection->data);
1920 prev_row = gtkut_ctree_get_nth_from_node
1921 (GTK_CMCTREE(ctree), node);
1924 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1927 if (prev_row != row) {
1928 gtk_cmclist_unselect_all(clist);
1929 if (event->button == 2)
1930 folderview_select_node
1932 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1935 gtk_cmclist_select_row(clist, row, column);
1939 if (event->button != 3) return FALSE;
1941 folderview_set_sens_and_popup_menu(folderview, row, event);
1945 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1946 FolderView *folderview)
1948 int row = -1, column = -1;
1950 if (!event) return FALSE;
1952 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1955 if (event->button == 1 && folderview->open_folder == FALSE &&
1956 folderview->opened != NULL) {
1957 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1958 folderview->opened);
1959 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1965 #define BREAK_ON_MODIFIER_KEY() \
1966 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1968 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1969 FolderView *folderview)
1971 if (!event) return FALSE;
1973 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
1976 switch (event->keyval) {
1978 #ifndef GENERIC_UMPC
1979 case GDK_KEY_Return:
1980 case GDK_KEY_KP_Enter:
1982 if (folderview->selected) {
1983 folderview_select_node(folderview,
1984 folderview->selected);
1988 case GDK_KEY_Return:
1989 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
1990 gtk_cmctree_toggle_expansion(
1991 GTK_CMCTREE(folderview->ctree),
1992 folderview->selected);
1997 BREAK_ON_MODIFIER_KEY();
1998 if (folderview->selected) {
1999 if (folderview->opened == folderview->selected &&
2000 (!folderview->summaryview->folder_item ||
2001 folderview->summaryview->folder_item->total_msgs == 0))
2002 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2004 folderview_select_node(folderview,
2005 folderview->selected);
2015 typedef struct _PostponedSelectData
2018 GtkCMCTreeNode *row;
2020 FolderView *folderview;
2021 } PostponedSelectData;
2023 static gboolean postpone_select(void *data)
2025 PostponedSelectData *psdata = (PostponedSelectData *)data;
2026 debug_print("trying again\n");
2028 psdata->folderview->postpone_select_id = 0;
2029 psdata->folderview->open_folder = TRUE;
2030 main_window_cursor_normal(psdata->folderview->mainwin);
2031 STATUSBAR_POP(psdata->folderview->mainwin);
2032 folderview_selected(psdata->ctree, psdata->row,
2033 psdata->column, psdata->folderview);
2038 void folderview_close_opened(FolderView *folderview)
2040 if (folderview->opened) {
2041 FolderItem *olditem;
2043 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2044 folderview->opened);
2046 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2047 olditem->path ? olditem->path:olditem->name);
2048 /* will be null if we just moved the previously opened folder */
2049 STATUSBAR_PUSH(folderview->mainwin, buf);
2050 main_window_cursor_wait(folderview->mainwin);
2052 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2053 summary_show(folderview->summaryview, NULL);
2054 folder_item_close(olditem);
2055 main_window_cursor_normal(folderview->mainwin);
2056 STATUSBAR_POP(folderview->mainwin);
2057 if (olditem->folder->klass->item_closed)
2058 olditem->folder->klass->item_closed(olditem);
2063 if (folderview->opened &&
2064 !GTK_CMCTREE_ROW(folderview->opened)->children)
2065 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2067 folderview->opened = NULL;
2069 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2070 gint column, FolderView *folderview)
2072 static gboolean can_select = TRUE; /* exclusive lock */
2077 GtkCMCTreeNode *old_opened = folderview->opened;
2079 folderview->selected = row;
2081 debug_print("newly selected %p, opened %p\n", folderview->selected,
2082 folderview->opened);
2083 if (folderview->opened == row) {
2084 folderview->open_folder = FALSE;
2089 item = gtk_cmctree_node_get_row_data(ctree, row);
2092 folderview->open_folder = FALSE;
2096 if (!can_select || summary_is_locked(folderview->summaryview)) {
2097 if (folderview->opened) {
2098 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2099 gtk_cmctree_select(ctree, folderview->opened);
2101 folderview->open_folder = FALSE;
2106 if (!folderview->open_folder) {
2113 /* Save cache for old folder */
2114 /* We don't want to lose all caches if sylpheed crashed */
2115 /* resets folderview->opened to NULL */
2116 folderview_close_opened(folderview);
2118 /* CLAWS: set compose button type: news folder items
2119 * always have a news folder as parent */
2121 toolbar_set_compose_button
2122 (folderview->mainwin->toolbar,
2123 FOLDER_TYPE(item->folder) == F_NEWS ?
2124 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2127 debug_print("Folder %s is selected\n", item->path);
2129 if (!GTK_CMCTREE_ROW(row)->children)
2130 gtk_cmctree_expand(ctree, row);
2132 /* ungrab the mouse event */
2133 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2134 gtk_grab_remove(GTK_WIDGET(ctree));
2135 if (gdk_pointer_is_grabbed())
2136 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2140 /* TODO: wwp: avoid displaying (null) in the status bar */
2141 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2142 item->path : "(null)");
2143 debug_print("%s\n", buf);
2144 STATUSBAR_PUSH(folderview->mainwin, buf);
2147 main_window_cursor_wait(folderview->mainwin);
2149 if (folderview->scanning_folder == item->folder) {
2152 res = folder_item_open(item);
2155 if (res == -1 && item->no_select == FALSE) {
2156 main_window_cursor_normal(folderview->mainwin);
2157 STATUSBAR_POP(folderview->mainwin);
2159 alertpanel_error(_("Folder could not be opened."));
2161 folderview->open_folder = FALSE;
2165 } else if (res == -2 && item->no_select == FALSE) {
2166 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2167 data->ctree = ctree;
2169 data->column = column;
2170 data->folderview = folderview;
2171 debug_print("postponing open of %s till end of scan\n",
2172 item->path ? item->path:item->name);
2173 folderview->open_folder = FALSE;
2175 if (folderview->postpone_select_id != 0)
2176 g_source_remove(folderview->postpone_select_id);
2177 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2182 main_window_cursor_normal(folderview->mainwin);
2185 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2186 opened = summary_show(folderview->summaryview, item);
2188 folder_clean_cache_memory(item);
2191 gtkut_ctree_set_focus_row(ctree, old_opened);
2192 gtk_cmctree_select(ctree, old_opened);
2193 folderview->opened = old_opened;
2195 folderview->opened = row;
2196 if (gtk_cmctree_node_is_visible(ctree, row)
2197 != GTK_VISIBILITY_FULL)
2198 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2201 STATUSBAR_POP(folderview->mainwin);
2203 folderview->open_folder = FALSE;
2208 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2209 FolderView *folderview)
2213 item = gtk_cmctree_node_get_row_data(ctree, node);
2214 cm_return_if_fail(item != NULL);
2215 item->collapsed = FALSE;
2216 folderview_update_node(folderview, node);
2219 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2220 FolderView *folderview)
2224 item = gtk_cmctree_node_get_row_data(ctree, node);
2225 cm_return_if_fail(item != NULL);
2226 item->collapsed = TRUE;
2227 folderview_update_node(folderview, node);
2230 static void folderview_popup_close(GtkMenuShell *menu_shell,
2231 FolderView *folderview)
2233 if (!folderview->opened) return;
2235 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2238 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2239 FolderView *folderview)
2241 FolderColumnType type = folderview->col_state[column].type;
2243 prefs_common.folder_col_size[type] = width;
2246 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2248 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2249 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2250 GtkCMCTreeNode *node, *parent_node;
2251 gint *col_pos = folderview->col_pos;
2252 FolderItemUpdateData hookdata;
2254 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2255 if (parent_node == NULL)
2258 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2260 text[col_pos[F_COL_FOLDER]] = item->name;
2261 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2266 gtk_cmctree_expand(ctree, parent_node);
2267 gtk_cmctree_node_set_row_data(ctree, node, item);
2269 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2270 folderview_sort_folders(folderview, parent_node, item->folder);
2272 hookdata.item = item;
2273 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2274 hookdata.msg = NULL;
2275 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2277 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2280 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2282 FolderView *folderview = (FolderView *)data;
2283 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2285 GSList *mlist = NULL;
2287 FolderItem *special_trash = NULL;
2290 if (!folderview->selected) return;
2291 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2292 cm_return_if_fail(item != NULL);
2293 cm_return_if_fail(item->folder != NULL);
2295 if (NULL != (ac = account_find_from_item(item)))
2296 special_trash = account_get_special_folder(ac, F_TRASH);
2298 if (item != item->folder->trash && item != special_trash
2299 && !folder_has_parent_of_type(item, F_TRASH)) return;
2301 if (prefs_common.ask_on_clean) {
2302 if (alertpanel(_("Empty trash"),
2303 _("Delete all messages in trash?"),
2304 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2308 mlist = folder_item_get_msg_list(item);
2310 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2311 MsgInfo * msginfo = (MsgInfo *) cur->data;
2312 if (MSG_IS_LOCKED(msginfo->flags))
2314 /* is it partially received? (partial_recv isn't cached) */
2315 if (msginfo->total_size != 0 &&
2316 msginfo->size != (off_t)msginfo->total_size)
2317 partial_mark_for_delete(msginfo);
2319 procmsg_msg_list_free(mlist);
2321 folder_item_remove_all_msg(item);
2324 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2326 FolderView *folderview = (FolderView *)data;
2327 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2329 FolderItem *special_queue = NULL;
2331 gchar *errstr = NULL;
2333 if (!folderview->selected) return;
2334 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2335 cm_return_if_fail(item != NULL);
2336 cm_return_if_fail(item->folder != NULL);
2338 if (NULL != (ac = account_find_from_item(item)))
2339 special_queue = account_get_special_folder(ac, F_QUEUE);
2341 if (item != item->folder->queue && item != special_queue
2342 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2344 if (procmsg_queue_is_empty(item))
2347 if (prefs_common.work_offline)
2348 if (alertpanel(_("Offline warning"),
2349 _("You're working offline. Override?"),
2350 GTK_STOCK_NO, GTK_STOCK_YES,
2351 NULL) != G_ALERTALTERNATE)
2354 /* ask for confirmation before sending queued messages only
2355 in online mode and if there is at least one message queued
2356 in any of the folder queue
2358 if (prefs_common.confirm_send_queued_messages) {
2359 if (!prefs_common.work_offline) {
2360 if (alertpanel(_("Send queued messages"),
2361 _("Send all queued messages?"),
2362 GTK_STOCK_CANCEL, _("_Send"),
2363 NULL) != G_ALERTALTERNATE)
2368 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2370 alertpanel_error_log(_("Some errors occurred while "
2371 "sending queued messages."));
2373 alertpanel_error_log(_("Some errors occurred "
2374 "while sending queued messages:\n%s"), errstr);
2380 static void folderview_search_cb(GtkAction *action, gpointer data)
2382 FolderView *folderview = (FolderView *)data;
2383 summary_search(folderview->summaryview);
2386 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2388 FolderView *folderview = (FolderView *)data;
2389 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2392 if (!folderview->selected) return;
2394 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2395 cm_return_if_fail(item != NULL);
2396 cm_return_if_fail(item->folder != NULL);
2398 item->processing_pending = TRUE;
2399 folder_item_apply_processing(item);
2400 item->processing_pending = FALSE;
2403 static void folderview_property_cb(GtkAction *action, gpointer data)
2405 FolderView *folderview = (FolderView *)data;
2406 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2409 if (!folderview->selected) return;
2411 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2412 cm_return_if_fail(item != NULL);
2413 cm_return_if_fail(item->folder != NULL);
2415 prefs_folder_item_open(item);
2418 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2420 GSList *list = NULL;
2421 GSList *done = NULL;
2422 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2424 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2425 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2426 && list->data != node) {
2427 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2428 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2431 for (list = done; list != NULL; list = g_slist_next(list)) {
2432 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2438 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2439 FolderItem *to_folder, gboolean copy)
2441 FolderItem *new_folder = NULL;
2445 cm_return_if_fail(folderview != NULL);
2446 cm_return_if_fail(from_folder != NULL);
2447 cm_return_if_fail(to_folder != NULL);
2449 if (prefs_common.warn_dnd) {
2450 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2451 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2452 from_folder->name, to_folder->name);
2453 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2454 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2455 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2458 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2460 else if (status & G_ALERTDISABLE)
2461 prefs_common.warn_dnd = FALSE;
2464 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2465 from_folder->name, to_folder->name);
2466 STATUSBAR_PUSH(folderview->mainwin, buf);
2468 summary_clear_all(folderview->summaryview);
2469 folderview->opened = NULL;
2470 folderview->selected = NULL;
2471 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2473 main_window_cursor_wait(folderview->mainwin);
2475 statusbar_verbosity_set(FALSE);
2476 folder_item_update_freeze();
2477 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2478 statusbar_verbosity_set(FALSE);
2479 main_window_cursor_normal(folderview->mainwin);
2480 STATUSBAR_POP(folderview->mainwin);
2481 folder_item_update_thaw();
2482 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2484 folderview_sort_folders(folderview,
2485 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2486 NULL, to_folder), new_folder->folder);
2487 folderview_select(folderview, new_folder);
2489 statusbar_verbosity_set(FALSE);
2490 main_window_cursor_normal(folderview->mainwin);
2491 STATUSBAR_POP(folderview->mainwin);
2492 folder_item_update_thaw();
2494 case F_MOVE_FAILED_DEST_IS_PARENT:
2495 alertpanel_error(_("Source and destination are the same."));
2497 case F_MOVE_FAILED_DEST_IS_CHILD:
2498 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2499 _("Can't move a folder to one of its children."));
2501 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2502 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2505 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2510 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2513 static gint folderview_clist_compare(GtkCMCList *clist,
2514 gconstpointer ptr1, gconstpointer ptr2)
2516 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2517 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2519 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2521 return item1->order - item2->order;
2524 // if only one folder has an order it comes first
2525 if (item1->order > 0)
2529 if (item2->order > 0)
2535 return (item2->name != NULL);
2539 return g_utf8_collate(item1->name, item2->name);
2542 static void folderview_processing_cb(GtkAction *action, gpointer data)
2544 FolderView *folderview = (FolderView *)data;
2545 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2549 if (!folderview->selected) return;
2551 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2552 cm_return_if_fail(item != NULL);
2553 cm_return_if_fail(item->folder != NULL);
2555 id = folder_item_get_identifier(item);
2556 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2559 prefs_filtering_open(&item->prefs->processing, title,
2560 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2564 void folderview_set_target_folder_color(gint color_op)
2568 FolderView *folderview;
2570 for (list = folderview_list; list != NULL; list = list->next) {
2571 folderview = (FolderView *)list->data;
2572 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2574 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2575 folderview->color_op;
2581 static gchar *last_smallfont = NULL;
2582 static gchar *last_normalfont = NULL;
2583 static gchar *last_boldfont = NULL;
2584 static gboolean last_derive = 0;
2586 void folderview_reinit_fonts(FolderView *folderview)
2589 g_free(last_smallfont);
2590 last_smallfont = NULL;
2591 g_free(last_normalfont);
2592 last_normalfont = NULL;
2593 g_free(last_boldfont);
2594 last_boldfont = NULL;
2597 void folderview_reflect_prefs(void)
2599 gboolean update_font = FALSE;
2600 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2601 FolderItem *item = folderview_get_selected_item(folderview);
2602 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2603 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2604 gint height = gtk_adjustment_get_value(pos);
2606 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2607 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2608 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2609 last_derive != prefs_common.derive_from_normal_font)
2612 g_free(last_smallfont);
2613 last_smallfont = g_strdup(SMALL_FONT);
2614 g_free(last_normalfont);
2615 last_normalfont = g_strdup(NORMAL_FONT);
2616 g_free(last_boldfont);
2617 last_boldfont = g_strdup(BOLD_FONT);
2618 last_derive = prefs_common.derive_from_normal_font;
2621 normal_style = normal_color_style = bold_style =
2622 bold_color_style = bold_tgtfold_style = NULL;
2624 folderview_init(folderview);
2626 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2627 folderview_column_set_titles(folderview);
2628 folderview_set_all();
2630 g_signal_handlers_block_by_func
2631 (G_OBJECT(folderview->ctree),
2632 G_CALLBACK(folderview_selected), folderview);
2635 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2636 GTK_CMCTREE(folderview->ctree), NULL, item);
2638 folderview_select(folderview, item);
2639 folderview->open_folder = FALSE;
2640 folderview->selected = node;
2643 g_signal_handlers_unblock_by_func
2644 (G_OBJECT(folderview->ctree),
2645 G_CALLBACK(folderview_selected), folderview);
2647 pos = gtk_scrolled_window_get_vadjustment(
2648 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2649 gtk_adjustment_set_value(pos, height);
2650 gtk_adjustment_changed(pos);
2651 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2654 static void drag_state_stop(FolderView *folderview)
2656 if (folderview->drag_timer_id)
2657 g_source_remove(folderview->drag_timer_id);
2658 folderview->drag_timer_id = 0;
2659 folderview->drag_node = NULL;
2662 static gboolean folderview_defer_expand(FolderView *folderview)
2664 if (folderview->drag_node) {
2665 folderview_recollapse_nodes(folderview, folderview->drag_node);
2666 if (folderview->drag_item->collapsed) {
2667 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2668 folderview->nodes_to_recollapse = g_slist_append
2669 (folderview->nodes_to_recollapse, folderview->drag_node);
2672 folderview->drag_item = NULL;
2673 folderview->drag_timer_id = 0;
2677 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2679 /* the idea is that we call drag_state_start() whenever we want expansion to
2680 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2681 * we need to call drag_state_stop() */
2682 drag_state_stop(folderview);
2683 /* request expansion */
2684 if (0 != (folderview->drag_timer_id = g_timeout_add
2685 (prefs_common.hover_timeout,
2686 (GSourceFunc)folderview_defer_expand,
2688 folderview->drag_node = node;
2689 folderview->drag_item = item;
2692 #ifndef GENERIC_UMPC
2693 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2694 FolderView *folderview)
2696 GdkDragContext *context;
2698 cm_return_if_fail(folderview != NULL);
2699 if (folderview->selected == NULL) return;
2700 if (folderview->nodes_to_recollapse)
2701 g_slist_free(folderview->nodes_to_recollapse);
2702 folderview->nodes_to_recollapse = NULL;
2703 context = gtk_drag_begin(widget, folderview->target_list,
2704 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2705 gtk_drag_set_icon_default(context);
2708 static void folderview_drag_data_get(GtkWidget *widget,
2709 GdkDragContext *drag_context,
2710 GtkSelectionData *selection_data,
2713 FolderView *folderview)
2717 gchar *source = NULL;
2718 if (info == TARGET_DUMMY) {
2719 sel = GTK_CMCLIST(folderview->ctree)->selection;
2723 item = gtk_cmctree_node_get_row_data
2724 (GTK_CMCTREE(folderview->ctree),
2725 GTK_CMCTREE_NODE(sel->data));
2727 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2728 gtk_selection_data_set(selection_data,
2729 gtk_selection_data_get_target(selection_data), 8,
2730 source, strlen(source));
2733 g_warning("unknown info %d\n", info);
2737 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2739 FolderUpdateData *hookdata;
2740 FolderView *folderview;
2744 folderview = (FolderView *) userdata;
2745 cm_return_val_if_fail(hookdata != NULL, FALSE);
2746 cm_return_val_if_fail(folderview != NULL, FALSE);
2748 ctree = folderview->ctree;
2749 cm_return_val_if_fail(ctree != NULL, FALSE);
2751 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2752 folderview_create_folder_node(folderview, hookdata->item);
2753 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2754 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2755 NULL, folder_item_parent(hookdata->item));
2756 folderview_sort_folders(folderview, node, hookdata->folder);
2757 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2758 GtkCMCTreeNode *node;
2760 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2762 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2763 if (folderview->selected == node)
2764 folderview->selected = NULL;
2765 if (folderview->opened == node)
2766 folderview->opened = NULL;
2768 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2769 /* do nothing, it's done by the ADD and REMOVE) */
2770 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2771 folderview_set(folderview);
2776 static gboolean folderview_dnd_scroll_cb(gpointer data)
2778 FolderView *folderview = (FolderView *)data;
2779 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2780 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2781 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2782 gint max = (int)gtk_adjustment_get_upper(pos) -
2783 (int)gtk_adjustment_get_page_size(pos);
2785 if (folderview->scroll_value == 0) {
2786 folderview->scroll_timeout_id = 0;
2790 if (folderview->scroll_value > 0 && new_val > max) {
2792 } else if (folderview->scroll_value < 0 && new_val < 0) {
2795 gtk_adjustment_set_value(pos, new_val);
2800 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2801 GdkDragContext *context,
2805 FolderView *folderview)
2808 FolderItem *item = NULL, *src_item = NULL;
2809 GtkCMCTreeNode *node = NULL;
2810 gboolean acceptable = FALSE;
2811 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2812 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2813 int height = (int)gtk_adjustment_get_page_size(pos);
2814 int total_height = (int)gtk_adjustment_get_upper(pos);
2815 int vpos = (int)gtk_adjustment_get_value(pos);
2816 int offset = prefs_common.show_col_headers ? 24:0;
2819 if (gtk_cmclist_get_selection_info
2820 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2821 GtkWidget *srcwidget;
2823 if (y > height - (48 - offset) && height + vpos < total_height) {
2824 dist = -(height - (48 - offset) - y);
2825 folderview->scroll_value = 1.41f * (1+(dist / 6));
2826 } else if (y < 72 - (24 - offset) && y >= 0) {
2827 dist = 72 - (24 - offset) - y;
2828 folderview->scroll_value = -1.41f * (1+(dist / 6));
2830 folderview->scroll_value = 0;
2832 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2833 folderview->scroll_timeout_id =
2834 g_timeout_add(30, folderview_dnd_scroll_cb,
2838 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2839 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2840 src_item = folderview->summaryview->folder_item;
2842 srcwidget = gtk_drag_get_source_widget(context);
2843 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2844 /* comes from summaryview */
2845 /* we are copying messages, so only accept folder items that are not
2846 the source item, are no root items and can copy messages */
2847 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2848 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2849 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2851 } else if (srcwidget == folderview->ctree) {
2852 /* comes from folderview */
2853 /* we are moving folder items, only accept folders that are not
2854 the source items and can copy messages and create folder items */
2855 if (item && item->folder && src_item && src_item != item &&
2856 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2857 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2858 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2859 || item->folder == src_item->folder))
2862 /* comes from another app */
2863 /* we are adding messages, so only accept folder items that are
2864 no root items and can copy messages */
2865 if (item && item->folder && folder_item_parent(item) != NULL
2866 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2867 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2872 if (acceptable || (src_item && src_item == item))
2873 drag_state_start(folderview, node, item);
2876 g_signal_handlers_block_by_func
2878 G_CALLBACK(folderview_selected), folderview);
2879 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2880 g_signal_handlers_unblock_by_func
2882 G_CALLBACK(folderview_selected), folderview);
2883 gdk_drag_status(context,
2884 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2885 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2887 if (folderview->opened)
2888 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2889 gdk_drag_status(context, 0, time);
2895 static void folderview_drag_leave_cb(GtkWidget *widget,
2896 GdkDragContext *context,
2898 FolderView *folderview)
2900 drag_state_stop(folderview);
2901 folderview->scroll_value = 0;
2902 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2905 static void free_info (gpointer stuff, gpointer data)
2910 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2911 guint time, FolderItem *item)
2914 GSList *msglist = NULL;
2915 list = uri_list_extract_filenames(data);
2916 if (!(item && item->folder && folder_item_parent(item) != NULL
2917 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2919 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2920 debug_print("item doesn't fit\n");
2924 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2925 debug_print("list is empty\n");
2928 for (tmp = list; tmp != NULL; tmp = tmp->next) {
2929 MsgFileInfo *info = NULL;
2931 if (file_is_email((gchar *)tmp->data)) {
2932 info = g_new0(MsgFileInfo, 1);
2933 info->msginfo = NULL;
2934 info->file = (gchar *)tmp->data;
2935 msglist = g_slist_prepend(msglist, info);
2936 debug_print("file is a mail\n");
2938 debug_print("file isn't a mail\n");
2942 msglist = g_slist_reverse(msglist);
2943 folder_item_add_msgs(item, msglist, FALSE);
2944 g_slist_foreach(msglist, free_info, NULL);
2945 g_slist_free(msglist);
2946 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2948 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2950 list_free_strings(list);
2954 static void folderview_drag_received_cb(GtkWidget *widget,
2955 GdkDragContext *drag_context,
2958 GtkSelectionData *data,
2961 FolderView *folderview)
2964 FolderItem *item = NULL, *src_item;
2965 GtkCMCTreeNode *node;
2966 int offset = prefs_common.show_col_headers ? 24:0;
2968 folderview->scroll_value = 0;
2970 if (info == TARGET_DUMMY) {
2971 drag_state_stop(folderview);
2972 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
2973 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
2974 /* comes from summaryview */
2975 if (gtk_cmclist_get_selection_info
2976 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
2979 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2980 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2981 src_item = folderview->summaryview->folder_item;
2983 if (item->no_select) {
2984 alertpanel_error(_("The destination folder can only be used to "
2985 "store subfolders."));
2988 /* re-check (due to acceptable possibly set for folder moves */
2989 if (!(item && item->folder && item->path && !item->no_select &&
2990 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
2993 if (item && src_item) {
2994 switch (gdk_drag_context_get_selected_action(drag_context)) {
2995 case GDK_ACTION_COPY:
2996 summary_copy_selected_to(folderview->summaryview, item);
2997 gtk_drag_finish(drag_context, TRUE, FALSE, time);
2999 case GDK_ACTION_MOVE:
3000 case GDK_ACTION_DEFAULT:
3002 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3003 summary_copy_selected_to(folderview->summaryview, item);
3005 summary_move_selected_to(folderview->summaryview, item);
3006 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3009 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3011 /* comes from folderview */
3013 gboolean folder_is_normal = TRUE;
3014 gboolean copy = (GDK_ACTION_COPY ==
3015 gdk_drag_context_get_selected_action(drag_context));
3017 source = (char *)gtk_selection_data_get_data(data) + 17;
3018 if (gtk_cmclist_get_selection_info
3019 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3021 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3024 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3025 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3026 src_item = folder_find_item_from_identifier(source);
3030 src_item->stype == F_NORMAL &&
3031 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3032 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3033 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3034 !folder_has_parent_of_type(src_item, F_TRASH);
3035 if (!item || !src_item || !folder_is_normal) {
3036 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3040 folderview_move_folder(folderview, src_item, item, copy);
3041 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3043 folderview->nodes_to_recollapse = NULL;
3044 } else if (info == TARGET_MAIL_URI_LIST) {
3045 if (gtk_cmclist_get_selection_info
3046 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3049 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3051 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3052 debug_print("no node\n");
3055 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3057 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3058 debug_print("no item\n");
3061 folderview_finish_dnd(gtk_selection_data_get_data(data),
3062 drag_context, time, item);
3066 static void folderview_drag_end_cb(GtkWidget *widget,
3067 GdkDragContext *drag_context,
3068 FolderView *folderview)
3070 drag_state_stop(folderview);
3071 folderview->scroll_value = 0;
3072 g_slist_free(folderview->nodes_to_recollapse);
3073 folderview->nodes_to_recollapse = NULL;
3076 void folderview_register_popup(FolderViewPopup *fpopup)
3080 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3081 FolderView *folderview = folderviews->data;
3082 GtkActionGroup *factory;
3084 factory = create_action_group(folderview, fpopup);
3085 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3087 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3090 void folderview_unregister_popup(FolderViewPopup *fpopup)
3095 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3096 FolderView *folderview = folderviews->data;
3098 g_hash_table_remove(folderview->popups, fpopup->klass);
3100 g_hash_table_remove(folderview_popups, fpopup->klass);