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_handler (GtkAction *action,
170 static void mark_all_read_cb (GtkAction *action,
172 static void mark_all_read_recursive_cb (GtkAction *action,
175 static void folderview_empty_trash_cb (GtkAction *action,
178 static void folderview_send_queue_cb (GtkAction *action,
181 static void folderview_search_cb (GtkAction *action,
183 static void folderview_run_processing_cb(GtkAction *action,
186 static void folderview_property_cb (GtkAction *action,
189 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
190 GdkDragContext *context,
194 FolderView *folderview);
195 static void folderview_drag_leave_cb (GtkWidget *widget,
196 GdkDragContext *context,
198 FolderView *folderview);
199 static void folderview_drag_received_cb (GtkWidget *widget,
200 GdkDragContext *drag_context,
203 GtkSelectionData *data,
206 FolderView *folderview);
208 static void folderview_start_drag (GtkWidget *widget, gint button, GdkEvent *event,
209 FolderView *folderview);
211 static void folderview_drag_data_get (GtkWidget *widget,
212 GdkDragContext *drag_context,
213 GtkSelectionData *selection_data,
216 FolderView *folderview);
217 static void folderview_drag_end_cb (GtkWidget *widget,
218 GdkDragContext *drag_context,
219 FolderView *folderview);
221 static void folderview_create_folder_node (FolderView *folderview,
223 static gboolean folderview_update_folder (gpointer source,
225 static gboolean folderview_update_item_claws (gpointer source,
227 static void folderview_processing_cb(GtkAction *action, gpointer data);
228 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
229 GdkEventButton *event);
231 GHashTable *folderview_popups;
233 static GtkActionEntry folderview_common_popup_entries[] =
235 {"FolderViewPopup", NULL, "FolderViewPopup" },
236 {"FolderViewPopup/MarkAllRead", NULL, N_("Mark all re_ad"), NULL, NULL, G_CALLBACK(mark_all_read_cb) },
237 {"FolderViewPopup/MarkAllReadRec", NULL, N_("Mark all read recursi_vely"), NULL, NULL, G_CALLBACK(mark_all_read_recursive_cb) },
238 {"FolderViewPopup/---", NULL, "---" },
239 {"FolderViewPopup/RunProcessing", NULL, N_("R_un processing rules"), NULL, NULL, G_CALLBACK(folderview_run_processing_cb) },
240 {"FolderViewPopup/SearchFolder", NULL, N_("_Search folder..."), NULL, NULL, G_CALLBACK(folderview_search_cb) },
241 {"FolderViewPopup/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(folderview_property_cb) },
242 {"FolderViewPopup/Processing", NULL, N_("Process_ing..."), NULL, NULL, G_CALLBACK(folderview_processing_cb) },
243 {"FolderViewPopup/EmptyTrash", NULL, N_("Empty _trash..."), NULL, NULL, G_CALLBACK(folderview_empty_trash_cb) },
244 {"FolderViewPopup/SendQueue", NULL, N_("Send _queue..."), NULL, NULL, G_CALLBACK(folderview_send_queue_cb) },
248 GtkTargetEntry folderview_drag_types[] =
250 {"claws-mail/internal", GTK_TARGET_SAME_APP, TARGET_DUMMY},
251 {"text/uri-list", 0, TARGET_MAIL_URI_LIST}
254 void folderview_initialize(void)
256 FolderViewPopup *fpopup;
258 fpopup = g_new0(FolderViewPopup, 1);
260 fpopup->klass = "common";
261 fpopup->path = "<CommonFolder>";
262 fpopup->entries = folderview_common_popup_entries;
263 fpopup->n_entries = G_N_ELEMENTS(folderview_common_popup_entries);
264 fpopup->set_sensitivity = NULL;
266 folderview_popups = g_hash_table_new(g_str_hash, g_str_equal);
267 g_hash_table_insert(folderview_popups, "common", fpopup);
270 static GtkActionGroup *create_action_group(FolderView *folderview, FolderViewPopup *fpopup)
272 FolderViewPopup *fpopup_common;
273 GtkActionGroup *action_group;
275 action_group = cm_menu_create_action_group(
277 fpopup->entries, fpopup->n_entries,
278 (gpointer)folderview);
280 if (fpopup->toggle_entries)
281 gtk_action_group_add_toggle_actions(action_group, fpopup->toggle_entries,
282 fpopup->n_toggle_entries,
283 (gpointer)folderview);
284 if (fpopup->radio_entries)
285 gtk_action_group_add_radio_actions(action_group, fpopup->radio_entries,
286 fpopup->n_radio_entries, fpopup->radio_default,
287 G_CALLBACK(fpopup->radio_callback),
288 (gpointer)folderview);
290 fpopup_common = g_hash_table_lookup(folderview_popups, "common");
291 if (fpopup_common != fpopup) {
292 gtk_action_group_add_actions(action_group, fpopup_common->entries,
293 fpopup_common->n_entries,
294 (gpointer)folderview);
295 if (fpopup_common->toggle_entries)
296 gtk_action_group_add_toggle_actions(action_group, fpopup_common->toggle_entries,
297 fpopup_common->n_toggle_entries,
298 (gpointer)folderview);
299 if (fpopup_common->radio_entries)
300 gtk_action_group_add_radio_actions(action_group, fpopup_common->radio_entries,
301 fpopup_common->n_radio_entries, fpopup_common->radio_default,
302 G_CALLBACK(fpopup_common->radio_callback),
303 (gpointer)folderview);
309 static void create_action_groups(gpointer key, gpointer value, gpointer data)
311 FolderView *folderview = data;
312 FolderViewPopup *fpopup = value;
313 GtkActionGroup *group;
315 group = create_action_group(folderview, fpopup);
316 g_hash_table_insert(folderview->popups, fpopup->klass, group);
319 static void folderview_column_set_titles(FolderView *folderview)
321 GtkWidget *ctree = folderview->ctree;
322 GtkWidget *label_folder;
323 GtkWidget *label_new;
324 GtkWidget *label_unread;
325 GtkWidget *label_total;
326 GtkWidget *hbox_folder;
328 GtkWidget *hbox_unread;
329 GtkWidget *hbox_total;
330 gint *col_pos = folderview->col_pos;
332 debug_print("setting titles...\n");
333 gtk_widget_realize(folderview->ctree);
334 gtk_widget_show_all(folderview->scrolledwin);
336 /* CLAWS: titles for "New" and "Unread" show new & unread pixmaps
337 * instead text (text overflows making them unreadable and ugly) */
338 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_NEW,
340 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_UNREAD,
342 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_READ,
345 label_folder = gtk_label_new(_("Folder"));
346 label_new = gtk_image_new_from_pixbuf(newxpm);
347 label_unread = gtk_image_new_from_pixbuf(unreadxpm);
348 label_total = gtk_image_new_from_pixbuf(readxpm);
350 gtk_cmclist_column_titles_active(GTK_CMCLIST(ctree));
352 hbox_folder = gtk_hbox_new(FALSE, 4);
353 hbox_new = gtk_hbox_new(FALSE, 4);
354 hbox_unread = gtk_hbox_new(FALSE, 4);
355 hbox_total = gtk_hbox_new(FALSE, 4);
358 gtk_box_pack_start(GTK_BOX(hbox_folder), label_folder, TRUE, TRUE, 0);
359 gtk_misc_set_alignment (GTK_MISC (label_folder), 0, 0.5);
360 gtk_box_pack_start(GTK_BOX(hbox_new), label_new, TRUE, TRUE, 0);
361 gtk_misc_set_alignment (GTK_MISC (label_new), 1, 0.5);
362 gtk_box_pack_start(GTK_BOX(hbox_unread), label_unread, TRUE, TRUE, 0);
363 gtk_misc_set_alignment (GTK_MISC (label_unread), 1, 0.5);
364 gtk_box_pack_start(GTK_BOX(hbox_total), label_total, TRUE, TRUE, 0);
365 gtk_misc_set_alignment (GTK_MISC (label_total), 1, 0.5);
367 gtk_widget_show_all(hbox_folder);
368 gtk_widget_show_all(hbox_new);
369 gtk_widget_show_all(hbox_unread);
370 gtk_widget_show_all(hbox_total);
373 gtk_widget_set_size_request(hbox_new, -1, 20);
374 gtk_widget_set_size_request(hbox_unread, -1, 20);
375 gtk_widget_set_size_request(hbox_total, -1, 20);
378 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_FOLDER],hbox_folder);
379 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_NEW],hbox_new);
380 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_UNREAD],hbox_unread);
381 gtk_cmclist_set_column_widget(GTK_CMCLIST(ctree),col_pos[F_COL_TOTAL],hbox_total);
387 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_NEW], _("New"));
388 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_UNREAD], _("Unread"));
389 gtk_sctree_set_column_tooltip(GTK_SCTREE(ctree), col_pos[F_COL_TOTAL], _("Total"));
392 static gboolean folderview_popup_menu(GtkWidget *widget, gpointer data)
394 FolderView *folderview = (FolderView *)data;
395 GdkEventButton event;
396 if (folderview_get_selected_item(folderview) == NULL)
400 event.time = gtk_get_current_event_time();
402 folderview_set_sens_and_popup_menu(folderview, -1,
409 static GtkWidget *folderview_ctree_create(FolderView *folderview)
413 FolderColumnState *col_state;
414 FolderColumnType type;
415 gchar *titles[N_FOLDER_COLS];
417 GtkWidget *scrolledwin = folderview->scrolledwin;
419 debug_print("creating tree...\n");
420 memset(titles, 0, sizeof(titles));
422 col_state = prefs_folder_column_get_config();
423 memset(titles, 0, sizeof(titles));
425 col_pos = folderview->col_pos;
427 for (i = 0; i < N_FOLDER_COLS; i++) {
428 folderview->col_state[i] = col_state[i];
429 type = col_state[i].type;
433 titles[col_pos[F_COL_FOLDER]] = _("Folder");
434 titles[col_pos[F_COL_NEW]] = _("New");
435 titles[col_pos[F_COL_UNREAD]] = _("Unread");
436 /* TRANSLATORS: This in Number sign in American style */
437 titles[col_pos[F_COL_TOTAL]] = _("#");
439 ctree = gtk_sctree_new_with_titles(N_FOLDER_COLS, col_pos[F_COL_FOLDER],
442 if (prefs_common.show_col_headers == FALSE)
443 gtk_cmclist_column_titles_hide(GTK_CMCLIST(ctree));
446 gtk_cmclist_set_selection_mode(GTK_CMCLIST(ctree), GTK_SELECTION_BROWSE);
447 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree), col_pos[F_COL_NEW],
449 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
450 col_pos[F_COL_UNREAD],
452 gtk_cmclist_set_column_justification(GTK_CMCLIST(ctree),
453 col_pos[F_COL_TOTAL],
455 gtk_cmctree_set_line_style(GTK_CMCTREE(ctree), GTK_CMCTREE_LINES_NONE);
456 gtk_cmctree_set_expander_style(GTK_CMCTREE(ctree),
457 GTK_CMCTREE_EXPANDER_TRIANGLE);
459 gtk_sctree_set_stripes(GTK_SCTREE(ctree), prefs_common.use_stripes_in_summaries);
460 gtk_sctree_set_recursive_expand(GTK_SCTREE(ctree), FALSE);
462 gtk_cmctree_set_indent(GTK_CMCTREE(ctree), CTREE_INDENT);
463 gtk_cmclist_set_compare_func(GTK_CMCLIST(ctree), folderview_clist_compare);
465 /* don't let title buttons take key focus */
466 for (i = 0; i < N_FOLDER_COLS; i++) {
467 gtkut_widget_set_can_focus(GTK_CMCLIST(ctree)->column[i].button, FALSE);
468 gtk_cmclist_set_column_width(GTK_CMCLIST(ctree), col_pos[i],
469 prefs_common.folder_col_size[i]);
470 gtk_cmclist_set_column_visibility
471 (GTK_CMCLIST(ctree), i, col_state[i].visible);
474 g_signal_connect(G_OBJECT(ctree), "key_press_event",
475 G_CALLBACK(folderview_key_pressed),
477 g_signal_connect(G_OBJECT(ctree), "button_press_event",
478 G_CALLBACK(folderview_button_pressed),
480 g_signal_connect(G_OBJECT(ctree), "popup-menu",
481 G_CALLBACK(folderview_popup_menu), folderview);
482 g_signal_connect(G_OBJECT(ctree), "button_release_event",
483 G_CALLBACK(folderview_button_released),
485 g_signal_connect(G_OBJECT(ctree), "tree_select_row",
486 G_CALLBACK(folderview_selected), folderview);
488 /* drag-n-dropping folders on maemo is impractical as this
489 * opens the folder almost everytime */
490 g_signal_connect(G_OBJECT(ctree), "start_drag",
491 G_CALLBACK(folderview_start_drag), folderview);
493 g_signal_connect(G_OBJECT(ctree), "drag_data_get",
494 G_CALLBACK(folderview_drag_data_get),
497 g_signal_connect_after(G_OBJECT(ctree), "tree_expand",
498 G_CALLBACK(folderview_tree_expanded),
500 g_signal_connect_after(G_OBJECT(ctree), "tree_collapse",
501 G_CALLBACK(folderview_tree_collapsed),
504 g_signal_connect(G_OBJECT(ctree), "resize_column",
505 G_CALLBACK(folderview_col_resized),
509 gtk_drag_dest_set(ctree, GTK_DEST_DEFAULT_ALL & ~GTK_DEST_DEFAULT_HIGHLIGHT,
510 folderview_drag_types, 2,
511 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_DEFAULT);
512 g_signal_connect(G_OBJECT(ctree), "drag_motion",
513 G_CALLBACK(folderview_drag_motion_cb),
515 g_signal_connect(G_OBJECT(ctree), "drag_leave",
516 G_CALLBACK(folderview_drag_leave_cb),
518 g_signal_connect(G_OBJECT(ctree), "drag_data_received",
519 G_CALLBACK(folderview_drag_received_cb),
521 g_signal_connect(G_OBJECT(ctree), "drag_end",
522 G_CALLBACK(folderview_drag_end_cb),
525 gtk_container_add(GTK_CONTAINER(scrolledwin), ctree);
530 void folderview_set_column_order(FolderView *folderview)
532 GtkWidget *ctree = folderview->ctree;
533 FolderItem *item = folderview_get_selected_item(folderview);
534 FolderItem *sel_item = NULL, *op_item = NULL;
535 GtkWidget *scrolledwin = folderview->scrolledwin;
537 if (folderview->drag_timer_id != 0) {
538 g_source_remove(folderview->drag_timer_id);
539 folderview->drag_timer_id = 0;
541 if (folderview->deferred_refresh_id != 0) {
542 g_source_remove(folderview->deferred_refresh_id);
543 folderview->deferred_refresh_id = 0;
545 if (folderview->scroll_timeout_id != 0) {
546 g_source_remove(folderview->scroll_timeout_id);
547 folderview->scroll_timeout_id = 0;
549 if (folderview->postpone_select_id != 0) {
550 g_source_remove(folderview->postpone_select_id);
551 folderview->postpone_select_id = 0;
554 if (folderview->selected)
555 sel_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->selected);
556 if (folderview->opened)
557 op_item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(ctree), folderview->opened);
559 debug_print("recreating tree...\n");
560 gtk_widget_destroy(folderview->ctree);
563 folderview->ctree = ctree = folderview_ctree_create(folderview);
564 gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
565 GTK_CMCLIST(ctree)->hadjustment);
566 gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(scrolledwin),
567 GTK_CMCLIST(ctree)->vadjustment);
568 gtk_widget_show(ctree);
571 folderview->selected = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, sel_item);
573 folderview->opened = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, op_item);
575 folderview_set(folderview);
576 folderview_column_set_titles(folderview);
578 folderview_select(folderview,item);
581 FolderView *folderview_create(void)
583 FolderView *folderview;
584 GtkWidget *scrolledwin;
587 debug_print("Creating folder view...\n");
588 folderview = g_new0(FolderView, 1);
590 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
591 gtk_scrolled_window_set_policy
592 (GTK_SCROLLED_WINDOW(scrolledwin),
593 GTK_POLICY_AUTOMATIC,
594 prefs_common.folderview_vscrollbar_policy);
595 gtk_widget_set_size_request(scrolledwin,
596 prefs_common.folderview_width,
597 prefs_common.folderview_height);
599 folderview->scrolledwin = scrolledwin;
600 ctree = folderview_ctree_create(folderview);
602 /* create popup factories */
603 folderview->popups = g_hash_table_new(g_str_hash, g_str_equal);
604 g_hash_table_foreach(folderview_popups, create_action_groups, folderview);
606 folderview->ctree = ctree;
608 folderview->folder_update_callback_id =
609 hooks_register_hook(FOLDER_UPDATE_HOOKLIST, folderview_update_folder, (gpointer) folderview);
610 folderview->folder_item_update_callback_id =
611 hooks_register_hook(FOLDER_ITEM_UPDATE_HOOKLIST, folderview_update_item_claws, (gpointer) folderview);
613 gtk_widget_show_all(scrolledwin);
615 folderview->target_list = gtk_target_list_new(folderview_drag_types, 2);
616 folderview_list = g_list_append(folderview_list, folderview);
618 folderview->drag_timer_id = 0;
619 folderview->deferred_refresh_id = 0;
620 folderview->scroll_timeout_id = 0;
621 folderview->postpone_select_id = 0;
626 void folderview_init(FolderView *folderview)
628 GtkWidget *ctree = folderview->ctree;
630 PangoFontDescription *normal_font;
632 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE, &inboxxpm);
633 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM, &inboxhrmxpm);
634 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN, &inboxopenxpm);
635 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM, &inboxopenhrmxpm);
636 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE, &outboxxpm);
637 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM, &outboxhrmxpm);
638 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN, &outboxopenxpm);
639 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM, &outboxopenhrmxpm);
640 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE, &folderxpm);
641 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM, &folderhrmxpm);
642 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN, &folderopenxpm);
643 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM, &folderopenhrmxpm);
644 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN, &trashopenxpm);
645 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM, &trashopenhrmxpm);
646 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE, &trashxpm);
647 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM, &trashhrmxpm);
648 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE, &queuexpm);
649 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM, &queuehrmxpm);
650 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN, &queueopenxpm);
651 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM, &queueopenhrmxpm);
652 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE, &draftsxpm);
653 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN, &draftsopenxpm);
654 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_NOSELECT, &noselectxpm);
656 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_MARK, &m_inboxxpm);
657 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_CLOSE_HRM_MARK, &m_inboxhrmxpm);
658 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_MARK, &m_inboxopenxpm);
659 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_INBOX_OPEN_HRM_MARK, &m_inboxopenhrmxpm);
660 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_MARK, &m_outboxxpm);
661 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_CLOSE_HRM_MARK, &m_outboxhrmxpm);
662 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_MARK, &m_outboxopenxpm);
663 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_OUTBOX_OPEN_HRM_MARK, &m_outboxopenhrmxpm);
664 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_MARK, &m_folderxpm);
665 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_CLOSE_HRM_MARK, &m_folderhrmxpm);
666 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_MARK, &m_folderopenxpm);
667 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DIR_OPEN_HRM_MARK, &m_folderopenhrmxpm);
668 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_MARK, &m_trashopenxpm);
669 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_OPEN_HRM_MARK, &m_trashopenhrmxpm);
670 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_MARK, &m_trashxpm);
671 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_TRASH_CLOSE_HRM_MARK, &m_trashhrmxpm);
672 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_MARK, &m_queuexpm);
673 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_CLOSE_HRM_MARK, &m_queuehrmxpm);
674 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_MARK, &m_queueopenxpm);
675 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_QUEUE_OPEN_HRM_MARK, &m_queueopenhrmxpm);
676 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_CLOSE_MARK, &m_draftsxpm);
677 stock_pixbuf_gdk(ctree, STOCK_PIXMAP_DRAFTS_OPEN_MARK, &m_draftsopenxpm);
679 normal_font = pango_font_description_from_string(NORMAL_FONT);
681 gtk_widget_modify_font(ctree, normal_font);
682 pango_font_description_free(normal_font);
684 gtk_cmclist_set_row_height(GTK_CMCLIST(ctree), 0);
687 PangoFontDescription *font_desc;
688 normal_style = gtk_style_copy(gtk_widget_get_style(ctree));
689 font_desc = pango_font_description_from_string(NORMAL_FONT);
691 if (normal_style->font_desc)
692 pango_font_description_free
693 (normal_style->font_desc);
694 normal_style->font_desc = font_desc;
696 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
697 normal_color_style = gtk_style_copy(normal_style);
698 normal_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
702 gtkut_convert_int_to_gdk_color(prefs_common.color_new, &gdk_color);
703 bold_style = gtk_style_copy(gtk_widget_get_style(ctree));
704 if (prefs_common.derive_from_normal_font || !BOLD_FONT) {
705 PangoFontDescription *font_desc;
706 font_desc = pango_font_description_from_string(NORMAL_FONT);
708 pango_font_description_free(bold_style->font_desc);
709 bold_style->font_desc = font_desc;
711 pango_font_description_set_weight
712 (bold_style->font_desc, PANGO_WEIGHT_BOLD);
714 PangoFontDescription *font_desc;
715 font_desc = pango_font_description_from_string(BOLD_FONT);
717 if (bold_style->font_desc)
718 pango_font_description_free
719 (bold_style->font_desc);
720 bold_style->font_desc = font_desc;
723 bold_color_style = gtk_style_copy(bold_style);
724 bold_color_style->fg[GTK_STATE_NORMAL] = gdk_color;
726 bold_tgtfold_style = gtk_style_copy(bold_style);
727 bold_tgtfold_style->fg[GTK_STATE_NORMAL] = folderview->color_op;
731 static gboolean folderview_defer_set(gpointer data)
733 FolderView *folderview = (FolderView *)data;
734 MainWindow *mainwin = folderview->mainwin;
738 if (mainwin->lock_count)
741 debug_print("doing deferred folderview_set now\n");
742 folderview_set(folderview);
744 folderview->deferred_refresh_id = 0;
748 void folderview_set(FolderView *folderview)
750 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
751 MainWindow *mainwin = folderview->mainwin;
752 FolderItem *sel_item = NULL, *op_item = NULL;
757 if (mainwin->lock_count) {
758 if (folderview->deferred_refresh_id == 0)
759 folderview->deferred_refresh_id =
760 g_timeout_add(500, folderview_defer_set, folderview);
761 debug_print("deferred folderview_set\n");
766 debug_print("Setting folder info...\n");
767 STATUSBAR_PUSH(mainwin, _("Setting folder info..."));
769 main_window_cursor_wait(mainwin);
771 if (folderview->selected)
772 sel_item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
773 if (folderview->opened)
774 op_item = gtk_cmctree_node_get_row_data(ctree, folderview->opened);
776 folderview->selected = NULL;
777 folderview->opened = NULL;
779 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
780 gtk_cmclist_clear(GTK_CMCLIST(ctree));
782 folderview_set_folders(folderview);
785 folderview->selected = gtk_cmctree_find_by_row_data(ctree, NULL, sel_item);
787 folderview->opened = gtk_cmctree_find_by_row_data(ctree, NULL, op_item);
789 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
790 main_window_cursor_normal(mainwin);
791 STATUSBAR_POP(mainwin);
795 void folderview_set_all(void)
799 for (list = folderview_list; list != NULL; list = list->next)
800 folderview_set((FolderView *)list->data);
803 void folderview_select(FolderView *folderview, FolderItem *item)
805 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
806 GtkCMCTreeNode *node;
807 GtkCMCTreeNode *old_selected = folderview->selected;
811 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
812 if (node) folderview_select_node(folderview, node);
814 if (old_selected != node)
815 folder_update_op_count();
818 static void mark_all_read_cb(GtkAction *action, gpointer data)
820 mark_all_read_handler(action, data, FALSE);
823 static void mark_all_read_recursive_cb(GtkAction *action, gpointer data)
825 mark_all_read_handler(action, data, TRUE);
828 static void mark_all_read_handler(GtkAction *action, gpointer data, gboolean recursive)
830 FolderView *folderview = (FolderView *)data;
835 item = folderview_get_selected_item(folderview);
839 message = recursive? _("Do you really want to mark all mails in this "
840 "folder and its sub-folders as read?") :
841 _("Do you really want to mark all mails in this "
843 if (folderview->summaryview->folder_item != item &&
844 prefs_common.ask_mark_all_read) {
845 val = alertpanel_full(_("Mark all as read"),
846 message, GTK_STOCK_NO, GTK_STOCK_YES, NULL,
847 TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
849 if ((val & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
851 else if (val & G_ALERTDISABLE)
852 prefs_common.ask_mark_all_read = FALSE;
855 folder_item_update_freeze();
856 if (folderview->summaryview->folder_item != item && !recursive)
857 summary_lock(folderview->summaryview);
859 summary_freeze(folderview->summaryview);
862 folderutils_mark_all_read_recursive(item);
864 folderutils_mark_all_read(item);
866 if (folderview->summaryview->folder_item != item && !recursive)
867 summary_unlock(folderview->summaryview);
869 summary_thaw(folderview->summaryview);
870 folder_item_update_thaw();
873 static void folderview_select_node(FolderView *folderview, GtkCMCTreeNode *node)
875 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
877 cm_return_if_fail(node != NULL);
879 if (folderview->open_folder) {
883 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
884 gtkut_ctree_expand_parent_all(ctree, node);
886 folderview->open_folder = TRUE;
887 gtkut_ctree_set_focus_row(ctree, node);
888 gtk_cmctree_select(ctree, node);
889 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
890 if ((folderview->summaryview->folder_item &&
891 folderview->summaryview->folder_item->total_msgs > 0) ||
892 prefs_common.layout_mode == SMALL_LAYOUT)
893 summary_grab_focus(folderview->summaryview);
895 gtk_widget_grab_focus(folderview->ctree);
898 void folderview_unselect(FolderView *folderview)
900 if (folderview->opened && !GTK_CMCTREE_ROW(folderview->opened)->children)
902 (GTK_CMCTREE(folderview->ctree), folderview->opened);
904 folderview->selected = folderview->opened = NULL;
907 static GtkCMCTreeNode *folderview_find_next_with_flag(GtkCMCTree *ctree,
908 GtkCMCTreeNode *node,
914 node = gtkut_ctree_node_next(ctree, node);
916 node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
918 for (; node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
919 item = gtk_cmctree_node_get_row_data(ctree, node);
922 if (item->stype == F_TRASH || item->stype == F_DRAFT)
926 if(item->unread_msgs > 0)
930 if(item->new_msgs > 0)
934 if(item->marked_msgs > 0)
943 void folderview_select_next_with_flag(FolderView *folderview,
947 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
948 GtkCMCTreeNode *node = NULL;
949 EntryAction last_summary_select_prio = prefs_common.summary_select_prio[0];
950 gboolean last_open = prefs_common.always_show_msg;
954 prefs_common.summary_select_prio[0] = ACTION_UNREAD;
957 prefs_common.summary_select_prio[0] = ACTION_NEW;
960 prefs_common.summary_select_prio[0] = ACTION_MARKED;
963 prefs_common.always_show_msg = force_open ? OPENMSG_ALWAYS : last_open;
965 node = folderview_find_next_with_flag(ctree, folderview->opened, flag);
967 folderview_select_node(folderview, node);
971 if (!folderview->opened ||
972 folderview->opened == GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list)) {
976 /* search again from the first node */
977 node = folderview_find_next_with_flag(ctree, NULL, flag);
979 folderview_select_node(folderview, node);
982 prefs_common.summary_select_prio[0] = last_summary_select_prio;
983 prefs_common.always_show_msg = last_open;
986 FolderItem *folderview_get_selected_item(FolderView *folderview)
988 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
990 if (!folderview->selected) return NULL;
991 return gtk_cmctree_node_get_row_data(ctree, folderview->selected);
994 static void folderview_set_folders(FolderView *folderview)
997 list = folder_get_list();
999 for (; list != NULL; list = list->next) {
1000 folderview_append_folder(folderview, FOLDER(list->data));
1004 static gchar *get_scan_str(FolderItem *item)
1007 return g_strdup_printf(_("Scanning folder %s%c%s..."),
1008 item->folder->name, G_DIR_SEPARATOR,
1011 return g_strdup_printf(_("Scanning folder %s..."),
1012 item->folder->name);
1014 static void folderview_scan_tree_func(Folder *folder, FolderItem *item,
1018 for (list = folderview_list; list != NULL; list = list->next) {
1019 FolderView *folderview = (FolderView *)list->data;
1020 MainWindow *mainwin = folderview->mainwin;
1021 gchar *str = get_scan_str(item);
1023 STATUSBAR_PUSH(mainwin, str);
1024 STATUSBAR_POP(mainwin);
1029 void folderview_rescan_tree(Folder *folder, gboolean rebuild)
1032 MainWindow *mainwin = mainwindow_get_mainwindow();
1033 FolderView *folderview = NULL;
1034 GtkAdjustment *pos = NULL;
1037 cm_return_if_fail(folder != NULL);
1039 if (!folder->klass->scan_tree) return;
1042 alertpanel_full(_("Rebuild folder tree"),
1043 _("Rebuilding the folder tree will remove "
1044 "local caches. Do you want to continue?"),
1045 GTK_STOCK_NO, GTK_STOCK_YES, NULL, FALSE,
1046 NULL, ALERT_WARNING, G_ALERTDEFAULT)
1047 != G_ALERTALTERNATE) {
1053 window = label_window_create(_("Rebuilding folder tree..."));
1055 window = label_window_create(_("Scanning folder tree..."));
1058 folderview = mainwin->folderview;
1061 pos = gtk_scrolled_window_get_vadjustment(
1062 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1063 height = gtk_adjustment_get_value(pos);
1066 folder_set_ui_func(folder, folderview_scan_tree_func, NULL);
1067 folder_scan_tree(folder, rebuild);
1068 folder_set_ui_func(folder, NULL, NULL);
1070 folderview_set_all();
1073 pos = gtk_scrolled_window_get_vadjustment(
1074 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
1075 gtk_adjustment_set_value(pos, height);
1076 gtk_adjustment_changed(pos);
1078 label_window_destroy(window);
1082 /** folderview_check_new()
1083 * Scan and update the folder and return the
1084 * count the number of new messages since last check.
1085 * \param folder the folder to check for new messages
1086 * \return the number of new messages since last check
1088 gint folderview_check_new(Folder *folder)
1092 FolderView *folderview;
1094 GtkCMCTreeNode *node;
1096 gint former_new_msgs = 0;
1097 gint former_new = 0, former_unread = 0, former_total;
1099 for (list = folderview_list; list != NULL; list = list->next) {
1100 folderview = (FolderView *)list->data;
1101 ctree = GTK_CMCTREE(folderview->ctree);
1102 folderview->scanning_folder = folder;
1104 main_window_lock(folderview->mainwin);
1106 for (node = GTK_CMCTREE_NODE(GTK_CMCLIST(ctree)->row_list);
1107 node != NULL; node = gtkut_ctree_node_next(ctree, node)) {
1109 item = gtk_cmctree_node_get_row_data(ctree, node);
1110 if (!item || !item->path || !item->folder) continue;
1111 if (item->no_select) continue;
1112 if (folder && folder != item->folder) continue;
1113 if (!folder && !FOLDER_IS_LOCAL(item->folder)) continue;
1114 if (!item->prefs->newmailcheck) continue;
1115 if (item->processing_pending == TRUE) {
1116 debug_print("skipping %s, processing pending\n",
1117 item->path ? item->path : item->name);
1120 if (item->scanning != ITEM_NOT_SCANNING) {
1121 debug_print("skipping %s, scanning\n",
1122 item->path ? item->path : item->name);
1126 str = get_scan_str(item);
1128 STATUSBAR_PUSH(folderview->mainwin, str);
1132 folderview_scan_tree_func(item->folder, item, NULL);
1133 former_new = item->new_msgs;
1134 former_unread = item->unread_msgs;
1135 former_total = item->total_msgs;
1137 if (item->folder->klass->scan_required &&
1138 (item->folder->klass->scan_required(item->folder, item) ||
1139 item->folder->inbox == item ||
1140 item->opened == TRUE ||
1141 item->processing_pending == TRUE)) {
1142 if (folder_item_scan(item) < 0) {
1144 summaryview_unlock(folderview->summaryview, item);
1145 if (FOLDER_TYPE(item->folder) == F_NEWS || FOLDER_IS_LOCAL(folder)) {
1146 log_error(LOG_PROTOCOL, _("Couldn't scan folder %s\n"),
1147 item->path ? item->path:item->name);
1148 STATUSBAR_POP(folderview->mainwin);
1150 } else if (!FOLDER_IS_LOCAL(folder)) {
1151 STATUSBAR_POP(folderview->mainwin);
1156 } else if (!item->folder->klass->scan_required) {
1157 if (folder_item_scan(item) < 0) {
1158 summaryview_unlock(folderview->summaryview, item);
1159 if (folder && !FOLDER_IS_LOCAL(folder)) {
1160 STATUSBAR_POP(folderview->mainwin);
1165 if (former_new != item->new_msgs ||
1166 former_unread != item->unread_msgs ||
1167 former_total != item->total_msgs)
1168 folderview_update_node(folderview, node);
1170 new_msgs += item->new_msgs;
1171 former_new_msgs += former_new;
1172 STATUSBAR_POP(folderview->mainwin);
1174 folderview->scanning_folder = NULL;
1175 main_window_unlock(folderview->mainwin);
1179 folder_write_list();
1180 /* Number of new messages since last check is the just the difference
1181 * between former_new_msgs and new_msgs. If new_msgs is less than
1182 * former_new_msgs, that would mean another session accessed the folder
1183 * and the result is not well defined.
1185 new_msgs = (former_new_msgs < new_msgs ? new_msgs - former_new_msgs : 0);
1189 void folderview_check_new_all(void)
1193 FolderView *folderview;
1195 folderview = (FolderView *)folderview_list->data;
1198 main_window_lock(folderview->mainwin);
1199 window = label_window_create
1200 (_("Checking for new messages in all folders..."));
1202 list = folder_get_list();
1203 for (; list != NULL; list = list->next) {
1204 Folder *folder = list->data;
1206 folderview_check_new(folder);
1209 folder_write_list();
1210 folderview_set_all();
1212 label_window_destroy(window);
1213 main_window_unlock(folderview->mainwin);
1217 static gboolean folderview_have_children_sub(FolderView *folderview,
1223 if (!item || !item->folder || !item->folder->node)
1226 node = item->folder->node;
1228 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1229 node = node->children;
1231 if (in_sub && item->total_msgs > 0) {
1235 while (node != NULL) {
1236 if (node && node->data) {
1237 FolderItem *next_item = (FolderItem*) node->data;
1239 if (folderview_have_children_sub(folderview,
1248 static gboolean folderview_have_children(FolderView *folderview,
1251 return folderview_have_children_sub(folderview, item, FALSE);
1254 static gboolean folderview_have_new_children_sub(FolderView *folderview,
1260 if (!item || !item->folder || !item->folder->node)
1263 node = item->folder->node;
1265 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1266 node = node->children;
1269 (item->new_msgs > 0 ||
1270 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1274 while (node != NULL) {
1275 if (node && node->data) {
1276 FolderItem *next_item = (FolderItem*) node->data;
1278 if (folderview_have_new_children_sub(folderview,
1287 static gboolean folderview_have_new_children(FolderView *folderview,
1290 return folderview_have_new_children_sub(folderview, item, FALSE);
1293 static gboolean folderview_have_unread_children_sub(FolderView *folderview,
1299 if (!item || !item->folder || !item->folder->node)
1302 node = item->folder->node;
1304 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1305 node = node->children;
1308 (item->unread_msgs > 0 ||
1309 (folder_has_parent_of_type(item, F_QUEUE) && item->total_msgs > 0))) {
1313 while (node != NULL) {
1314 if (node && node->data) {
1315 FolderItem *next_item = (FolderItem*) node->data;
1317 if (folderview_have_unread_children_sub(folderview,
1327 static gboolean folderview_have_unread_children(FolderView *folderview,
1330 return folderview_have_unread_children_sub(folderview, item, FALSE);
1333 static gboolean folderview_have_matching_children_sub(FolderView *folderview,
1339 if (!item || !item->folder || !item->folder->node)
1342 node = item->folder->node;
1344 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1345 node = node->children;
1347 if (in_sub && item->search_match){
1351 while (node != NULL) {
1352 if (node && node->data) {
1353 FolderItem *next_item = (FolderItem*) node->data;
1355 if (folderview_have_matching_children_sub(folderview,
1365 static gboolean folderview_have_matching_children(FolderView *folderview,
1368 return folderview_have_matching_children_sub(folderview, item, FALSE);
1371 static gboolean folderview_have_marked_children_sub(FolderView *folderview,
1377 if (!item || !item->folder || !item->folder->node)
1380 node = item->folder->node;
1382 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
1383 node = node->children;
1385 if (item->marked_msgs != 0) {
1389 while (node != NULL) {
1390 if (node && node->data) {
1391 FolderItem *next_item = (FolderItem*) node->data;
1393 if (folderview_have_marked_children_sub(folderview,
1402 static gboolean folderview_have_marked_children(FolderView *folderview,
1405 return folderview_have_marked_children_sub(folderview, item, FALSE);
1408 static void folderview_update_node(FolderView *folderview, GtkCMCTreeNode *node)
1410 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1411 GtkStyle *style = NULL;
1412 GtkStyle *color_style = NULL;
1414 GdkPixbuf *xpm, *openxpm;
1415 static GdkPixbuf *searchicon;
1416 gboolean mark = FALSE;
1419 gboolean add_unread_mark;
1420 gboolean add_sub_match_mark;
1421 gboolean use_bold, use_color;
1422 gint *col_pos = folderview->col_pos;
1423 SpecialFolderItemType stype;
1425 item = gtk_cmctree_node_get_row_data(ctree, node);
1426 cm_return_if_fail(item != NULL);
1428 if (!GTK_CMCTREE_ROW(node)->expanded)
1429 mark = folderview_have_marked_children(folderview, item);
1431 mark = (item->marked_msgs != 0);
1433 stype = item->stype;
1434 if (stype == F_NORMAL) {
1435 if (folder_has_parent_of_type(item, F_TRASH))
1437 else if (folder_has_parent_of_type(item, F_DRAFT))
1439 else if (folder_has_parent_of_type(item, F_OUTBOX))
1441 else if (folder_has_parent_of_type(item, F_QUEUE))
1446 if (item->hide_read_msgs || item->hide_read_threads) {
1447 xpm = mark?m_inboxhrmxpm:inboxhrmxpm;
1448 openxpm = mark?m_inboxopenhrmxpm:inboxopenhrmxpm;
1450 xpm = mark?m_inboxxpm:inboxxpm;
1451 openxpm = mark?m_inboxopenxpm:inboxopenxpm;
1455 if (item->hide_read_msgs || item->hide_read_threads) {
1456 xpm = mark?m_outboxhrmxpm:outboxhrmxpm;
1457 openxpm = mark?m_outboxopenhrmxpm:outboxopenhrmxpm;
1459 xpm = mark?m_outboxxpm:outboxxpm;
1460 openxpm = mark?m_outboxopenxpm:outboxopenxpm;
1464 if (item->hide_read_msgs || item->hide_read_threads) {
1465 xpm = mark?m_queuehrmxpm:queuehrmxpm;
1466 openxpm = mark?m_queueopenhrmxpm:queueopenhrmxpm;
1468 xpm = mark?m_queuexpm:queuexpm;
1469 openxpm = mark?m_queueopenxpm:queueopenxpm;
1473 if (item->hide_read_msgs || item->hide_read_threads) {
1474 xpm = mark?m_trashhrmxpm:trashhrmxpm;
1475 openxpm = mark?m_trashopenhrmxpm:trashopenhrmxpm;
1477 xpm = mark?m_trashxpm:trashxpm;
1478 openxpm = mark?m_trashopenxpm:trashopenxpm;
1482 xpm = mark?m_draftsxpm:draftsxpm;
1483 openxpm = mark?m_draftsopenxpm:draftsopenxpm;
1486 if (item->hide_read_msgs || item->hide_read_threads) {
1487 xpm = mark?m_folderhrmxpm:folderhrmxpm;
1488 openxpm = mark?m_folderopenhrmxpm:folderopenhrmxpm;
1490 xpm = mark?m_folderxpm:folderxpm;
1491 openxpm = mark?m_folderopenxpm:folderopenxpm;
1495 if (item->no_select) {
1496 xpm = openxpm = noselectxpm;
1499 name = folder_item_get_name(item);
1501 if (!GTK_CMCTREE_ROW(node)->expanded) {
1502 add_unread_mark = folderview_have_unread_children(
1504 add_sub_match_mark = folderview_have_matching_children(
1507 add_unread_mark = FALSE;
1508 add_sub_match_mark = FALSE;
1511 if (item->search_match) {
1513 stock_pixbuf_gdk(folderview->ctree, STOCK_PIXMAP_QUICKSEARCH,
1516 xpm = openxpm = searchicon;
1520 if (prefs_common.display_folder_unread) {
1521 if (folder_has_parent_of_type(item, F_QUEUE)) {
1522 /* only total_msgs matters here */
1523 if (item->total_msgs > 0) {
1524 /* show total number (should be equal to the unread number)
1526 str = g_strdup_printf("%s (%d%s%s)",
1527 name, item->total_msgs,
1528 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1529 (item->unreadmarked_msgs > 0) ? "!" : "");
1532 if (prefs_common.display_folder_unread == 1) {
1533 if (item->unread_msgs > 0) {
1534 /* show unread number and signs */
1535 str = g_strdup_printf("%s (%d%s%s)",
1536 name, item->unread_msgs,
1537 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1538 (item->unreadmarked_msgs > 0) ? "!" : "");
1541 if (item->total_msgs > 0) {
1542 /* show unread number, total number and signs if any */
1543 str = g_strdup_printf("%s (%d/%d%s%s)",
1544 name, item->unread_msgs, item->total_msgs,
1545 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1546 (item->unreadmarked_msgs > 0) ? "!" : "");
1550 if ((str == NULL) &&
1551 (add_unread_mark || add_sub_match_mark || (item->unreadmarked_msgs > 0))) {
1552 /* no unread/total numbers, but at least one sign */
1553 str = g_strdup_printf("%s (%s%s)",
1555 (add_unread_mark || add_sub_match_mark) ? "+" : "",
1556 (item->unreadmarked_msgs > 0) ? "!" : "");
1560 /* last fallback, folder name only or with +! sign */
1561 if (item->unreadmarked_msgs > 0 && add_sub_match_mark) {
1562 str = g_strdup_printf("%s%s",
1564 } else if (item->unreadmarked_msgs > 0) {
1565 str = g_strdup_printf("%s%s",
1567 } else if (add_sub_match_mark) {
1568 str = g_strdup_printf("%s%s",
1571 str = g_strdup_printf("%s", name);
1574 gtk_cmctree_set_node_info(ctree, node, str, FOLDER_SPACING,
1576 FALSE, GTK_CMCTREE_ROW(node)->expanded);
1580 if (!folder_item_parent(item)) {
1581 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], "-");
1582 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], "-");
1583 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], "-");
1585 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_NEW], item->new_msgs > 0 ? itos(item->new_msgs) : prefs_common.zero_replacement);
1586 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_UNREAD], item->unread_msgs > 0 ? itos(item->unread_msgs) : prefs_common.zero_replacement);
1587 gtk_cmctree_node_set_text(ctree, node, col_pos[F_COL_TOTAL], item->total_msgs > 0 ? itos(item->total_msgs) : prefs_common.zero_replacement);
1590 if (folder_has_parent_of_type(item, F_OUTBOX) ||
1591 folder_has_parent_of_type(item, F_TRASH)) {
1592 use_bold = use_color = FALSE;
1593 } else if (folder_has_parent_of_type(item, F_QUEUE)) {
1594 GSList *list = folder_item_get_msg_list(item);
1596 use_bold = use_color = FALSE;
1597 for (cur = list; cur; cur = cur->next) {
1598 MsgInfo *msginfo = (MsgInfo *)cur->data;
1599 if (!MSG_IS_DELETED(msginfo->flags)) {
1600 /* highlight queue folder if there are any messages */
1601 use_bold = use_color = TRUE;
1605 if (!GTK_CMCTREE_ROW(node)->expanded &&
1606 use_bold == FALSE &&
1607 folderview_have_children(folderview, item))
1608 use_bold = use_color = TRUE;
1609 procmsg_msg_list_free(list);
1611 /* if unread messages exist, print with bold font */
1612 use_bold = (item->unread_msgs > 0|| item->new_msgs > 0)
1614 /* if new messages exist, print with colored letter */
1616 (item->new_msgs > 0) ||
1618 folderview_have_new_children(folderview, item));
1621 gtk_cmctree_node_set_foreground(ctree, node, NULL);
1626 if (item->prefs->color > 0 && !use_color) {
1627 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1628 color_style = gtk_style_copy(bold_style);
1629 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1630 style = color_style;
1631 } else if (use_color) {
1632 style = bold_color_style;
1635 if (item->op_count > 0) {
1636 style = bold_tgtfold_style;
1638 } else if (use_color) {
1639 style = normal_color_style;
1640 gtk_cmctree_node_set_foreground(ctree, node,
1641 &folderview->color_new);
1642 } else if (item->op_count > 0) {
1643 style = bold_tgtfold_style;
1644 } else if (item->prefs->color > 0) {
1646 gtkut_convert_int_to_gdk_color(item->prefs->color, &gdk_color);
1647 color_style = gtk_style_copy(normal_style);
1648 color_style->fg[GTK_STATE_NORMAL] = gdk_color;
1649 style = color_style;
1651 style = normal_style;
1654 gtk_cmctree_node_set_row_style(ctree, node, style);
1656 if ((node = gtkut_ctree_find_collapsed_parent(ctree, node)) != NULL)
1657 folderview_update_node(folderview, node);
1660 void folderview_update_search_icon(FolderItem *item, gboolean matches)
1663 FolderView *folderview;
1665 GtkCMCTreeNode *node;
1667 cm_return_if_fail(item != NULL);
1669 for (list = folderview_list; list != NULL; list = list->next) {
1670 folderview = (FolderView *)list->data;
1671 ctree = GTK_CMCTREE(folderview->ctree);
1673 node = gtk_cmctree_find_by_row_data(ctree, NULL, item);
1674 if (node && item->search_match != matches) {
1675 item->search_match = matches;
1676 folderview_update_node(folderview, node);
1681 static gboolean folderview_update_item_claws(gpointer source, gpointer data)
1683 FolderItemUpdateData *update_info = (FolderItemUpdateData *)source;
1684 FolderView *folderview = (FolderView *)data;
1686 GtkCMCTreeNode *node;
1687 cm_return_val_if_fail(update_info != NULL, TRUE);
1688 cm_return_val_if_fail(update_info->item != NULL, TRUE);
1689 cm_return_val_if_fail(folderview != NULL, FALSE);
1691 ctree = GTK_CMCTREE(folderview->ctree);
1693 node = gtk_cmctree_find_by_row_data(ctree, NULL, update_info->item);
1696 if (update_info->update_flags & (F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_NAME))
1697 folderview_update_node(folderview, node);
1699 if ((update_info->update_flags & F_ITEM_UPDATE_CONTENT) &&
1700 update_info->item == folderview->summaryview->folder_item &&
1701 update_info->item != NULL)
1702 if (!quicksearch_has_sat_predicate(folderview->summaryview->quicksearch))
1703 summary_show(folderview->summaryview, update_info->item);
1709 static gboolean folderview_gnode_func(GtkCMCTree *ctree, guint depth,
1710 GNode *gnode, GtkCMCTreeNode *cnode,
1713 FolderView *folderview = (FolderView *)data;
1714 FolderItem *item = FOLDER_ITEM(gnode->data);
1716 cm_return_val_if_fail(item != NULL, FALSE);
1718 gtk_cmctree_node_set_row_data(ctree, cnode, item);
1719 folderview_update_node(folderview, cnode);
1724 static void folderview_expand_func(GtkCMCTree *ctree, GtkCMCTreeNode *node,
1727 FolderView *folderview = (FolderView *)data;
1730 if (GTK_CMCTREE_ROW(node)->children) {
1731 item = gtk_cmctree_node_get_row_data(ctree, node);
1732 cm_return_if_fail(item != NULL);
1734 if (!item->collapsed)
1735 gtk_cmctree_expand(ctree, node);
1737 folderview_update_node(folderview, node);
1741 static void set_special_folder(GtkCMCTree *ctree, FolderItem *item,
1742 GtkCMCTreeNode *root, GtkCMCTreeNode **prev)
1745 GtkCMCTreeNode *node, *parent, *sibling;
1747 node = gtk_cmctree_find_by_row_data(ctree, root, item);
1749 g_warning("%s not found.", item->path);
1751 parent = GTK_CMCTREE_ROW(node)->parent;
1752 if (*prev && parent == GTK_CMCTREE_ROW(*prev)->parent)
1753 sibling = GTK_CMCTREE_ROW(*prev)->sibling;
1755 sibling = GTK_CMCTREE_ROW(parent)->children;
1759 tmp = gtk_cmctree_node_get_row_data
1761 if (tmp && tmp->stype != F_NORMAL)
1762 sibling = GTK_CMCTREE_ROW(sibling)->sibling;
1766 if (node != sibling)
1767 gtk_cmctree_move(ctree, node, parent, sibling);
1774 static void folderview_sort_folders(FolderView *folderview, GtkCMCTreeNode *root,
1777 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1778 GtkCMCTreeNode *prev = NULL;
1780 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
1781 gtk_sctree_sort_recursive(ctree, root);
1782 if (root && GTK_CMCTREE_ROW(root)->parent) {
1783 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1786 set_special_folder(ctree, folder->inbox, root, &prev);
1787 set_special_folder(ctree, folder->outbox, root, &prev);
1788 set_special_folder(ctree, folder->draft, root, &prev);
1789 set_special_folder(ctree, folder->queue, root, &prev);
1790 set_special_folder(ctree, folder->trash, root, &prev);
1791 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
1794 static void folderview_append_folder(FolderView *folderview, Folder *folder)
1796 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
1797 GtkCMCTreeNode *root;
1799 cm_return_if_fail(folder != NULL);
1801 root = gtk_sctree_insert_gnode(ctree, NULL, NULL, folder->node,
1802 folderview_gnode_func, folderview);
1803 gtk_cmctree_pre_recursive(ctree, root, folderview_expand_func,
1805 folderview_sort_folders(folderview, root, folder);
1808 /* callback functions */
1809 static void folderview_set_sens_and_popup_menu(FolderView *folderview, gint row,
1810 GdkEventButton *event)
1814 FolderViewPopup *fpopup;
1815 GtkActionGroup *action_group;
1817 FolderItem *special_trash = NULL, *special_queue = NULL;
1819 GtkUIManager *ui_manager = gtk_ui_manager_new();
1821 if (folderview->ui_manager)
1822 g_object_unref(folderview->ui_manager);
1824 folderview->ui_manager = ui_manager;
1825 item = folderview_get_selected_item(folderview);
1827 cm_return_if_fail(item != NULL);
1828 cm_return_if_fail(item->folder != NULL);
1829 folder = item->folder;
1831 fpopup = g_hash_table_lookup(folderview_popups, folder->klass->idstr);
1834 action_group = g_hash_table_lookup(folderview->popups, folder->klass->idstr);
1836 fpopup = g_hash_table_lookup(folderview_popups, "common");
1837 action_group = g_hash_table_lookup(folderview->popups, "common");
1840 gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
1841 MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Popup", "Popup", GTK_UI_MANAGER_MENUBAR)
1842 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup", "FolderViewPopup", "FolderViewPopup", GTK_UI_MANAGER_MENU)
1844 if (fpopup->add_menuitems)
1845 fpopup->add_menuitems(ui_manager, item);
1847 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllRead", "FolderViewPopup/MarkAllRead", GTK_UI_MANAGER_MENUITEM)
1848 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MarkAllReadRec", "FolderViewPopup/MarkAllReadRec", GTK_UI_MANAGER_MENUITEM)
1849 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Separator1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1850 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RunProcessing", "FolderViewPopup/RunProcessing", GTK_UI_MANAGER_MENUITEM)
1851 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SearchFolder", "FolderViewPopup/SearchFolder", GTK_UI_MANAGER_MENUITEM)
1852 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Properties", "FolderViewPopup/Properties", GTK_UI_MANAGER_MENUITEM)
1853 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "Processing", "FolderViewPopup/Processing", GTK_UI_MANAGER_MENUITEM)
1855 if (fpopup->set_sensitivity != NULL)
1856 fpopup->set_sensitivity(ui_manager, item);
1858 if (NULL != (ac = account_find_from_item(item))) {
1859 special_trash = account_get_special_folder(ac, F_TRASH);
1860 special_queue = account_get_special_folder(ac, F_QUEUE);
1863 if ((item == folder->trash || item == special_trash
1864 || folder_has_parent_of_type(item, F_TRASH))) {
1865 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorTrash", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1866 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "EmptyTrash", "FolderViewPopup/EmptyTrash", GTK_UI_MANAGER_MENUITEM)
1869 if ((item == folder->queue || item == special_queue
1870 || folder_has_parent_of_type(item, F_QUEUE))) {
1871 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorQueue", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1872 MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SendQueue", "FolderViewPopup/SendQueue", GTK_UI_MANAGER_MENUITEM)
1875 #define SET_SENS(name, sens) \
1876 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1878 SET_SENS("FolderViewPopup/MarkAllRead", item->unread_msgs >= 1);
1879 SET_SENS("FolderViewPopup/MarkAllReadRec", folderview_have_unread_children(folderview,item));
1880 SET_SENS("FolderViewPopup/SearchFolder", item->total_msgs >= 1 &&
1881 folderview->selected == folderview->opened);
1882 SET_SENS("FolderViewPopup/Properties", TRUE);
1884 SET_SENS("FolderViewPopup/RunProcessing", item->prefs->processing &&
1885 item->total_msgs >= 1 && !item->processing_pending);
1886 SET_SENS("FolderViewPopup/Processing", item->node->parent != NULL &&
1887 !item->no_select && !item->processing_pending);
1889 if (item == folder->trash || item == special_trash
1890 || folder_has_parent_of_type(item, F_TRASH)) {
1891 GSList *msglist = folder_item_get_msg_list(item);
1892 SET_SENS("FolderViewPopup/EmptyTrash", msglist != NULL);
1893 procmsg_msg_list_free(msglist);
1895 if (item == folder->queue || item == special_queue
1896 || folder_has_parent_of_type(item, F_QUEUE)) {
1897 GSList *msglist = folder_item_get_msg_list(item);
1898 SET_SENS("FolderViewPopup/SendQueue", msglist != NULL);
1899 procmsg_msg_list_free(msglist);
1903 popup = gtk_menu_item_get_submenu(GTK_MENU_ITEM(
1904 gtk_ui_manager_get_widget(ui_manager, "/Popup/FolderViewPopup")) );
1905 g_signal_connect(G_OBJECT(popup), "selection_done",
1906 G_CALLBACK(folderview_popup_close),
1908 gtk_menu_popup(GTK_MENU(popup), NULL, NULL, NULL, NULL,
1909 event->button, event->time);
1912 static gboolean folderview_button_pressed(GtkWidget *ctree, GdkEventButton *event,
1913 FolderView *folderview)
1915 GtkCMCList *clist = GTK_CMCLIST(ctree);
1916 gint prev_row = -1, row = -1, column = -1;
1918 if (!event) return FALSE;
1920 if (event->button == 1 || event->button == 2) {
1921 if (!gtk_sctree_is_hot_spot (GTK_SCTREE(clist), event->x, event->y))
1922 folderview->open_folder = TRUE;
1924 if (event->type == GDK_2BUTTON_PRESS) {
1925 if (clist->selection) {
1926 GtkCMCTreeNode *node;
1928 node = GTK_CMCTREE_NODE(clist->selection->data);
1930 gtk_cmctree_toggle_expansion(
1933 folderview->open_folder = FALSE;
1940 if (event->button == 2 || event->button == 3) {
1942 if (clist->selection) {
1943 GtkCMCTreeNode *node;
1945 node = GTK_CMCTREE_NODE(clist->selection->data);
1947 prev_row = gtkut_ctree_get_nth_from_node
1948 (GTK_CMCTREE(ctree), node);
1951 if (!gtk_cmclist_get_selection_info(clist, event->x, event->y,
1954 if (prev_row != row) {
1955 gtk_cmclist_unselect_all(clist);
1956 if (event->button == 2)
1957 folderview_select_node
1959 gtk_cmctree_node_nth(GTK_CMCTREE(ctree),
1962 gtk_cmclist_select_row(clist, row, column);
1966 if (event->button != 3) return FALSE;
1968 folderview_set_sens_and_popup_menu(folderview, row, event);
1972 static gboolean folderview_button_released(GtkWidget *ctree, GdkEventButton *event,
1973 FolderView *folderview)
1975 int row = -1, column = -1;
1977 if (!event) return FALSE;
1979 if (!gtk_cmclist_get_selection_info(GTK_CMCLIST(ctree), event->x, event->y,
1982 if (event->button == 1 && folderview->open_folder == FALSE &&
1983 folderview->opened != NULL) {
1984 gtkut_ctree_set_focus_row(GTK_CMCTREE(ctree),
1985 folderview->opened);
1986 gtk_cmctree_select(GTK_CMCTREE(ctree), folderview->opened);
1992 #define BREAK_ON_MODIFIER_KEY() \
1993 if ((event->state & (GDK_MOD1_MASK|GDK_CONTROL_MASK)) != 0) break
1995 static gboolean folderview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1996 FolderView *folderview)
1998 GtkCMCTreeNode *node;
2001 if (!event) return FALSE;
2003 if (quicksearch_has_focus(folderview->summaryview->quicksearch))
2006 switch (event->keyval) {
2008 if (folderview->selected) {
2009 if (GTK_CMCTREE_ROW(folderview->selected)->children != NULL
2010 && !GTK_CMCTREE_ROW(folderview->selected)->expanded)
2011 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree),
2012 folderview->selected);
2014 folderview_select_node(folderview,
2015 folderview->selected);
2019 case GDK_KEY_Return:
2020 if (folderview->selected && GTK_CMCTREE_ROW(folderview->selected)->children) {
2021 gtk_cmctree_toggle_expansion(
2022 GTK_CMCTREE(folderview->ctree),
2023 folderview->selected);
2027 case GDK_KEY_Return:
2028 case GDK_KEY_KP_Enter:
2029 if (folderview->selected)
2030 folderview_select_node(folderview, folderview->selected);
2034 BREAK_ON_MODIFIER_KEY();
2035 if (folderview->selected) {
2036 if (folderview->opened == folderview->selected &&
2037 (!folderview->summaryview->folder_item ||
2038 folderview->summaryview->folder_item->total_msgs == 0))
2039 folderview_select_next_with_flag(folderview, MSG_UNREAD, TRUE);
2041 folderview_select_node(folderview,
2042 folderview->selected);
2046 if (folderview->selected) {
2047 if (GTK_CMCTREE_ROW(folderview->selected)->expanded) {
2048 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree),
2049 folderview->selected);
2051 if ((item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2052 folderview->selected))) {
2053 if ((node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2054 NULL, folder_item_parent(item)))) {
2055 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), node);
2056 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2057 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2066 if (event->keyval == GDK_KEY_Home)
2067 node = gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0);
2069 node = gtk_cmctree_last(GTK_CMCTREE(folderview->ctree),
2070 gtk_cmctree_node_nth(GTK_CMCTREE(folderview->ctree), 0));
2072 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), node);
2074 if (!gtk_cmctree_node_is_visible(GTK_CMCTREE(folderview->ctree), node))
2075 gtk_cmctree_node_moveto(GTK_CMCTREE(folderview->ctree),
2085 typedef struct _PostponedSelectData
2088 GtkCMCTreeNode *row;
2090 FolderView *folderview;
2091 } PostponedSelectData;
2093 static gboolean postpone_select(void *data)
2095 PostponedSelectData *psdata = (PostponedSelectData *)data;
2096 debug_print("trying again\n");
2098 psdata->folderview->postpone_select_id = 0;
2099 psdata->folderview->open_folder = TRUE;
2100 main_window_cursor_normal(psdata->folderview->mainwin);
2101 STATUSBAR_POP(psdata->folderview->mainwin);
2102 folderview_selected(psdata->ctree, psdata->row,
2103 psdata->column, psdata->folderview);
2108 void folderview_close_opened(FolderView *folderview)
2110 if (folderview->opened) {
2111 FolderItem *olditem;
2113 olditem = gtk_cmctree_node_get_row_data(GTK_CMCTREE(folderview->ctree),
2114 folderview->opened);
2116 gchar *buf = g_strdup_printf(_("Closing folder %s..."),
2117 olditem->path ? olditem->path:olditem->name);
2118 /* will be null if we just moved the previously opened folder */
2119 STATUSBAR_PUSH(folderview->mainwin, buf);
2120 main_window_cursor_wait(folderview->mainwin);
2122 summary_save_prefs_to_folderitem(folderview->summaryview, olditem);
2123 summary_show(folderview->summaryview, NULL);
2124 folder_item_close(olditem);
2125 main_window_cursor_normal(folderview->mainwin);
2126 STATUSBAR_POP(folderview->mainwin);
2127 if (olditem->folder->klass->item_closed)
2128 olditem->folder->klass->item_closed(olditem);
2133 if (folderview->opened &&
2134 !GTK_CMCTREE_ROW(folderview->opened)->children)
2135 gtk_cmctree_collapse(GTK_CMCTREE(folderview->ctree), folderview->opened);
2137 folderview->opened = NULL;
2139 static void folderview_selected(GtkCMCTree *ctree, GtkCMCTreeNode *row,
2140 gint column, FolderView *folderview)
2142 static gboolean can_select = TRUE; /* exclusive lock */
2147 GtkCMCTreeNode *old_opened = folderview->opened;
2149 folderview->selected = row;
2151 debug_print("newly selected %p, opened %p\n", folderview->selected,
2152 folderview->opened);
2153 if (folderview->opened == row) {
2154 folderview->open_folder = FALSE;
2159 item = gtk_cmctree_node_get_row_data(ctree, row);
2162 folderview->open_folder = FALSE;
2166 if (!can_select || summary_is_locked(folderview->summaryview)) {
2167 if (folderview->opened) {
2168 gtkut_ctree_set_focus_row(ctree, folderview->opened);
2169 gtk_cmctree_select(ctree, folderview->opened);
2171 folderview->open_folder = FALSE;
2176 if (!folderview->open_folder) {
2183 /* Save cache for old folder */
2184 /* We don't want to lose all caches if sylpheed crashed */
2185 /* resets folderview->opened to NULL */
2186 folderview_close_opened(folderview);
2188 /* CLAWS: set compose button type: news folder items
2189 * always have a news folder as parent */
2191 toolbar_set_compose_button
2192 (folderview->mainwin->toolbar,
2193 FOLDER_TYPE(item->folder) == F_NEWS ?
2194 COMPOSEBUTTON_NEWS : COMPOSEBUTTON_MAIL);
2197 debug_print("Folder %s is selected\n", item->path);
2199 if (!GTK_CMCTREE_ROW(row)->children)
2200 gtk_cmctree_expand(ctree, row);
2202 /* ungrab the mouse event */
2203 if (gtk_widget_has_grab(GTK_WIDGET(ctree))) {
2204 gtk_grab_remove(GTK_WIDGET(ctree));
2205 if (gdk_pointer_is_grabbed())
2206 gdk_pointer_ungrab(GDK_CURRENT_TIME);
2210 /* TODO: wwp: avoid displaying (null) in the status bar */
2211 buf = g_strdup_printf(_("Opening folder %s..."), item->path ?
2212 item->path : "(null)");
2213 debug_print("%s\n", buf);
2214 STATUSBAR_PUSH(folderview->mainwin, buf);
2217 main_window_cursor_wait(folderview->mainwin);
2219 if (folderview->scanning_folder == item->folder) {
2222 res = folder_item_open(item);
2225 if (res == -1 && item->no_select == FALSE) {
2226 main_window_cursor_normal(folderview->mainwin);
2227 STATUSBAR_POP(folderview->mainwin);
2229 alertpanel_error(_("Folder could not be opened."));
2231 folderview->open_folder = FALSE;
2235 } else if (res == -2 && item->no_select == FALSE) {
2236 PostponedSelectData *data = g_new0(PostponedSelectData, 1);
2237 data->ctree = ctree;
2239 data->column = column;
2240 data->folderview = folderview;
2241 debug_print("postponing open of %s till end of scan\n",
2242 item->path ? item->path:item->name);
2243 folderview->open_folder = FALSE;
2245 if (folderview->postpone_select_id != 0)
2246 g_source_remove(folderview->postpone_select_id);
2247 folderview->postpone_select_id = g_timeout_add(500, postpone_select, data);
2252 main_window_cursor_normal(folderview->mainwin);
2255 summary_set_prefs_from_folderitem(folderview->summaryview, item);
2256 opened = summary_show(folderview->summaryview, item);
2258 folder_clean_cache_memory(item);
2261 gtkut_ctree_set_focus_row(ctree, old_opened);
2262 gtk_cmctree_select(ctree, old_opened);
2263 folderview->opened = old_opened;
2265 folderview->opened = row;
2266 if (gtk_cmctree_node_is_visible(ctree, row)
2267 != GTK_VISIBILITY_FULL)
2268 gtk_cmctree_node_moveto(ctree, row, -1, 0.5, 0);
2271 STATUSBAR_POP(folderview->mainwin);
2273 folderview->open_folder = FALSE;
2278 static void folderview_tree_expanded(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2279 FolderView *folderview)
2283 item = gtk_cmctree_node_get_row_data(ctree, node);
2284 cm_return_if_fail(item != NULL);
2285 item->collapsed = FALSE;
2286 folderview_update_node(folderview, node);
2289 static void folderview_tree_collapsed(GtkCMCTree *ctree, GtkCMCTreeNode *node,
2290 FolderView *folderview)
2294 item = gtk_cmctree_node_get_row_data(ctree, node);
2295 cm_return_if_fail(item != NULL);
2296 item->collapsed = TRUE;
2297 folderview_update_node(folderview, node);
2300 static void folderview_popup_close(GtkMenuShell *menu_shell,
2301 FolderView *folderview)
2303 if (!folderview->opened) return;
2305 gtk_cmctree_select(GTK_CMCTREE(folderview->ctree), folderview->opened);
2308 static void folderview_col_resized(GtkCMCList *clist, gint column, gint width,
2309 FolderView *folderview)
2311 FolderColumnType type = folderview->col_state[column].type;
2313 prefs_common.folder_col_size[type] = width;
2316 static void folderview_create_folder_node(FolderView *folderview, FolderItem *item)
2318 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2319 gchar *text[N_FOLDER_COLS] = {NULL, "0", "0", "0"};
2320 GtkCMCTreeNode *node, *parent_node;
2321 gint *col_pos = folderview->col_pos;
2322 FolderItemUpdateData hookdata;
2324 parent_node = gtk_cmctree_find_by_row_data(ctree, NULL, folder_item_parent(item));
2325 if (parent_node == NULL)
2328 gtk_cmclist_freeze(GTK_CMCLIST(ctree));
2330 text[col_pos[F_COL_FOLDER]] = item->name;
2331 node = gtk_sctree_insert_node(ctree, parent_node, NULL, text,
2336 gtk_cmctree_expand(ctree, parent_node);
2337 gtk_cmctree_node_set_row_data(ctree, node, item);
2339 gtk_cmctree_node_set_row_style(ctree, node, normal_style);
2340 folderview_sort_folders(folderview, parent_node, item->folder);
2342 hookdata.item = item;
2343 hookdata.update_flags = F_ITEM_UPDATE_NAME;
2344 hookdata.msg = NULL;
2345 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
2347 gtk_cmclist_thaw(GTK_CMCLIST(ctree));
2350 static void folderview_empty_trash_cb(GtkAction *action, gpointer data)
2352 FolderView *folderview = (FolderView *)data;
2353 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2355 GSList *mlist = NULL;
2357 FolderItem *special_trash = NULL;
2360 if (!folderview->selected) return;
2361 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2362 cm_return_if_fail(item != NULL);
2363 cm_return_if_fail(item->folder != NULL);
2365 if (NULL != (ac = account_find_from_item(item)))
2366 special_trash = account_get_special_folder(ac, F_TRASH);
2368 if (item != item->folder->trash && item != special_trash
2369 && !folder_has_parent_of_type(item, F_TRASH)) return;
2371 if (prefs_common.ask_on_clean) {
2372 if (alertpanel(_("Empty trash"),
2373 _("Delete all messages in trash?"),
2374 GTK_STOCK_CANCEL, _("+_Empty trash"), NULL) != G_ALERTALTERNATE)
2378 mlist = folder_item_get_msg_list(item);
2380 for (cur = mlist ; cur != NULL ; cur = cur->next) {
2381 MsgInfo * msginfo = (MsgInfo *) cur->data;
2382 if (MSG_IS_LOCKED(msginfo->flags))
2384 /* is it partially received? (partial_recv isn't cached) */
2385 if (msginfo->total_size != 0 &&
2386 msginfo->size != (off_t)msginfo->total_size)
2387 partial_mark_for_delete(msginfo);
2389 procmsg_msg_list_free(mlist);
2391 folder_item_remove_all_msg(item);
2394 static void folderview_send_queue_cb(GtkAction *action, gpointer data)
2396 FolderView *folderview = (FolderView *)data;
2397 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2399 FolderItem *special_queue = NULL;
2401 gchar *errstr = NULL;
2403 if (!folderview->selected) return;
2404 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2405 cm_return_if_fail(item != NULL);
2406 cm_return_if_fail(item->folder != NULL);
2408 if (NULL != (ac = account_find_from_item(item)))
2409 special_queue = account_get_special_folder(ac, F_QUEUE);
2411 if (item != item->folder->queue && item != special_queue
2412 && !folder_has_parent_of_type(item, F_QUEUE)) return;
2414 if (procmsg_queue_is_empty(item))
2417 if (prefs_common.work_offline)
2418 if (alertpanel(_("Offline warning"),
2419 _("You're working offline. Override?"),
2420 GTK_STOCK_NO, GTK_STOCK_YES,
2421 NULL) != G_ALERTALTERNATE)
2424 /* ask for confirmation before sending queued messages only
2425 in online mode and if there is at least one message queued
2426 in any of the folder queue
2428 if (prefs_common.confirm_send_queued_messages) {
2429 if (!prefs_common.work_offline) {
2430 if (alertpanel(_("Send queued messages"),
2431 _("Send all queued messages?"),
2432 GTK_STOCK_CANCEL, _("_Send"),
2433 NULL) != G_ALERTALTERNATE)
2438 if (procmsg_send_queue(item, prefs_common.savemsg, &errstr) < 0) {
2440 alertpanel_error_log(_("Some errors occurred while "
2441 "sending queued messages."));
2443 alertpanel_error_log(_("Some errors occurred "
2444 "while sending queued messages:\n%s"), errstr);
2450 static void folderview_search_cb(GtkAction *action, gpointer data)
2452 FolderView *folderview = (FolderView *)data;
2453 summary_search(folderview->summaryview);
2456 static void folderview_run_processing_cb(GtkAction *action, gpointer data)
2458 FolderView *folderview = (FolderView *)data;
2459 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2462 if (!folderview->selected) return;
2464 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2465 cm_return_if_fail(item != NULL);
2466 cm_return_if_fail(item->folder != NULL);
2468 item->processing_pending = TRUE;
2469 folder_item_apply_processing(item);
2470 item->processing_pending = FALSE;
2473 static void folderview_property_cb(GtkAction *action, gpointer data)
2475 FolderView *folderview = (FolderView *)data;
2476 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2479 if (!folderview->selected) return;
2481 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2482 cm_return_if_fail(item != NULL);
2483 cm_return_if_fail(item->folder != NULL);
2485 prefs_folder_item_open(item);
2488 static void folderview_recollapse_nodes(FolderView *folderview, GtkCMCTreeNode *node)
2490 GSList *list = NULL;
2491 GSList *done = NULL;
2492 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2494 for (list = folderview->nodes_to_recollapse; list != NULL; list = g_slist_next(list)) {
2495 if (!gtkut_ctree_node_is_parent(GTK_CMCTREE_NODE(list->data), node)
2496 && list->data != node) {
2497 gtk_cmctree_collapse(ctree, GTK_CMCTREE_NODE(list->data));
2498 done = g_slist_append(done, GTK_CMCTREE_NODE(list->data));
2501 for (list = done; list != NULL; list = g_slist_next(list)) {
2502 folderview->nodes_to_recollapse = g_slist_remove(folderview->nodes_to_recollapse,
2508 void folderview_move_folder(FolderView *folderview, FolderItem *from_folder,
2509 FolderItem *to_folder, gboolean copy)
2511 FolderItem *new_folder = NULL;
2515 cm_return_if_fail(folderview != NULL);
2516 cm_return_if_fail(from_folder != NULL);
2517 cm_return_if_fail(to_folder != NULL);
2519 if (prefs_common.warn_dnd) {
2520 buf = g_strdup_printf(copy ? _("Do you really want to copy folder '%s' in '%s'?"):
2521 _("Do you really want to make folder '%s' a subfolder of '%s'?"),
2522 from_folder->name, to_folder->name);
2523 status = alertpanel_full(copy ? _("Copy folder"):_("Move folder"), buf,
2524 GTK_STOCK_NO, GTK_STOCK_YES, NULL, TRUE,
2525 NULL, ALERT_QUESTION, G_ALERTDEFAULT);
2528 if ((status & ~G_ALERTDISABLE) != G_ALERTALTERNATE)
2530 else if (status & G_ALERTDISABLE)
2531 prefs_common.warn_dnd = FALSE;
2534 buf = g_strdup_printf(copy ? _("Copying %s to %s..."):_("Moving %s to %s..."),
2535 from_folder->name, to_folder->name);
2536 STATUSBAR_PUSH(folderview->mainwin, buf);
2538 summary_clear_all(folderview->summaryview);
2539 folderview->opened = NULL;
2540 folderview->selected = NULL;
2541 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), FALSE);
2543 main_window_cursor_wait(folderview->mainwin);
2545 statusbar_verbosity_set(FALSE);
2546 folder_item_update_freeze();
2547 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2548 if ((status = folder_item_move_to(from_folder, to_folder, &new_folder, copy)) == F_MOVE_OK) {
2549 statusbar_verbosity_set(FALSE);
2550 main_window_cursor_normal(folderview->mainwin);
2551 STATUSBAR_POP(folderview->mainwin);
2552 folder_item_update_thaw();
2553 folder_item_update_recursive(new_folder, F_ITEM_UPDATE_MSGCNT);
2555 folderview_sort_folders(folderview,
2556 gtk_cmctree_find_by_row_data(GTK_CMCTREE(folderview->ctree),
2557 NULL, to_folder), new_folder->folder);
2558 folderview_select(folderview, new_folder);
2559 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2561 statusbar_verbosity_set(FALSE);
2562 main_window_cursor_normal(folderview->mainwin);
2563 STATUSBAR_POP(folderview->mainwin);
2564 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2565 folder_item_update_thaw();
2567 case F_MOVE_FAILED_DEST_IS_PARENT:
2568 alertpanel_error(_("Source and destination are the same."));
2570 case F_MOVE_FAILED_DEST_IS_CHILD:
2571 alertpanel_error(copy ? _("Can't copy a folder to one of its children."):
2572 _("Can't move a folder to one of its children."));
2574 case F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX:
2575 alertpanel_error(_("A folder cannot be moved between different mailboxes."));
2578 alertpanel_error(copy ? _("Copy failed!"):_("Move failed!"));
2583 gtk_widget_set_sensitive(GTK_WIDGET(folderview->ctree), TRUE);
2586 static gint folderview_clist_compare(GtkCMCList *clist,
2587 gconstpointer ptr1, gconstpointer ptr2)
2589 FolderItem *item1 = ((GtkCMCListRow *)ptr1)->data;
2590 FolderItem *item2 = ((GtkCMCListRow *)ptr2)->data;
2592 if (item1->order > 0 && item2->order > 0) // if we have an order item, use it
2594 return item1->order - item2->order;
2597 // if only one folder has an order it comes first
2598 if (item1->order > 0)
2602 if (item2->order > 0)
2608 return (item2->name != NULL);
2612 return g_utf8_collate(item1->name, item2->name);
2615 static void folderview_processing_cb(GtkAction *action, gpointer data)
2617 FolderView *folderview = (FolderView *)data;
2618 GtkCMCTree *ctree = GTK_CMCTREE(folderview->ctree);
2622 if (!folderview->selected) return;
2624 item = gtk_cmctree_node_get_row_data(ctree, folderview->selected);
2625 cm_return_if_fail(item != NULL);
2626 cm_return_if_fail(item->folder != NULL);
2628 id = folder_item_get_identifier(item);
2629 title = g_strdup_printf (_("Processing configuration for folder %s"), id);
2632 prefs_filtering_open(&item->prefs->processing, title,
2633 MANUAL_ANCHOR_PROCESSING, NULL, NULL, FALSE);
2637 void folderview_set_target_folder_color(gint color_op)
2641 FolderView *folderview;
2643 for (list = folderview_list; list != NULL; list = list->next) {
2644 folderview = (FolderView *)list->data;
2645 gtkut_convert_int_to_gdk_color(color_op, &folderview->color_op);
2647 bold_tgtfold_style->fg[GTK_STATE_NORMAL] =
2648 folderview->color_op;
2654 static gchar *last_smallfont = NULL;
2655 static gchar *last_normalfont = NULL;
2656 static gchar *last_boldfont = NULL;
2657 static gboolean last_derive = 0;
2659 void folderview_reinit_fonts(FolderView *folderview)
2662 g_free(last_smallfont);
2663 last_smallfont = NULL;
2664 g_free(last_normalfont);
2665 last_normalfont = NULL;
2666 g_free(last_boldfont);
2667 last_boldfont = NULL;
2670 void folderview_reflect_prefs(void)
2672 gboolean update_font = FALSE;
2673 FolderView *folderview = mainwindow_get_mainwindow()->folderview;
2674 FolderItem *item = folderview_get_selected_item(folderview);
2675 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2676 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2677 gint height = gtk_adjustment_get_value(pos);
2679 if (!last_smallfont || strcmp(last_smallfont, SMALL_FONT) ||
2680 !last_normalfont || strcmp(last_normalfont, NORMAL_FONT) ||
2681 !last_boldfont || strcmp(last_boldfont, BOLD_FONT) ||
2682 last_derive != prefs_common.derive_from_normal_font)
2685 g_free(last_smallfont);
2686 last_smallfont = g_strdup(SMALL_FONT);
2687 g_free(last_normalfont);
2688 last_normalfont = g_strdup(NORMAL_FONT);
2689 g_free(last_boldfont);
2690 last_boldfont = g_strdup(BOLD_FONT);
2691 last_derive = prefs_common.derive_from_normal_font;
2694 normal_style = normal_color_style = bold_style =
2695 bold_color_style = bold_tgtfold_style = NULL;
2697 folderview_init(folderview);
2699 gtk_cmclist_freeze(GTK_CMCLIST(folderview->ctree));
2700 folderview_column_set_titles(folderview);
2701 folderview_set_all();
2703 g_signal_handlers_block_by_func
2704 (G_OBJECT(folderview->ctree),
2705 G_CALLBACK(folderview_selected), folderview);
2708 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(
2709 GTK_CMCTREE(folderview->ctree), NULL, item);
2711 folderview_select(folderview, item);
2712 folderview->open_folder = FALSE;
2713 folderview->selected = node;
2716 g_signal_handlers_unblock_by_func
2717 (G_OBJECT(folderview->ctree),
2718 G_CALLBACK(folderview_selected), folderview);
2720 pos = gtk_scrolled_window_get_vadjustment(
2721 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2722 gtk_adjustment_set_value(pos, height);
2723 gtk_adjustment_changed(pos);
2724 gtk_cmclist_thaw(GTK_CMCLIST(folderview->ctree));
2727 static void drag_state_stop(FolderView *folderview)
2729 if (folderview->drag_timer_id)
2730 g_source_remove(folderview->drag_timer_id);
2731 folderview->drag_timer_id = 0;
2732 folderview->drag_node = NULL;
2735 static gboolean folderview_defer_expand(FolderView *folderview)
2737 if (folderview->drag_node) {
2738 folderview_recollapse_nodes(folderview, folderview->drag_node);
2739 if (folderview->drag_item->collapsed) {
2740 gtk_cmctree_expand(GTK_CMCTREE(folderview->ctree), folderview->drag_node);
2741 folderview->nodes_to_recollapse = g_slist_append
2742 (folderview->nodes_to_recollapse, folderview->drag_node);
2745 folderview->drag_item = NULL;
2746 folderview->drag_timer_id = 0;
2750 static void drag_state_start(FolderView *folderview, GtkCMCTreeNode *node, FolderItem *item)
2752 /* the idea is that we call drag_state_start() whenever we want expansion to
2753 * start after 'prefs_common.hover_time' msecs. if we want to cancel expansion,
2754 * we need to call drag_state_stop() */
2755 drag_state_stop(folderview);
2756 /* request expansion */
2757 if (0 != (folderview->drag_timer_id = g_timeout_add
2758 (prefs_common.hover_timeout,
2759 (GSourceFunc)folderview_defer_expand,
2761 folderview->drag_node = node;
2762 folderview->drag_item = item;
2765 #ifndef GENERIC_UMPC
2766 static void folderview_start_drag(GtkWidget *widget, gint button, GdkEvent *event,
2767 FolderView *folderview)
2769 GdkDragContext *context;
2771 cm_return_if_fail(folderview != NULL);
2772 if (folderview->selected == NULL) return;
2773 if (folderview->nodes_to_recollapse)
2774 g_slist_free(folderview->nodes_to_recollapse);
2775 folderview->nodes_to_recollapse = NULL;
2776 context = gtk_drag_begin(widget, folderview->target_list,
2777 GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_DEFAULT, button, event);
2778 gtk_drag_set_icon_default(context);
2781 static void folderview_drag_data_get(GtkWidget *widget,
2782 GdkDragContext *drag_context,
2783 GtkSelectionData *selection_data,
2786 FolderView *folderview)
2790 gchar *source = NULL;
2791 if (info == TARGET_DUMMY) {
2792 sel = GTK_CMCLIST(folderview->ctree)->selection;
2796 item = gtk_cmctree_node_get_row_data
2797 (GTK_CMCTREE(folderview->ctree),
2798 GTK_CMCTREE_NODE(sel->data));
2800 source = g_strdup_printf ("FROM_OTHER_FOLDER%s", folder_item_get_identifier(item));
2801 gtk_selection_data_set(selection_data,
2802 gtk_selection_data_get_target(selection_data), 8,
2803 source, strlen(source));
2806 g_warning("unknown info %d", info);
2810 static gboolean folderview_update_folder(gpointer source, gpointer userdata)
2812 FolderUpdateData *hookdata;
2813 FolderView *folderview;
2817 folderview = (FolderView *) userdata;
2818 cm_return_val_if_fail(hookdata != NULL, FALSE);
2819 cm_return_val_if_fail(folderview != NULL, FALSE);
2821 ctree = folderview->ctree;
2822 cm_return_val_if_fail(ctree != NULL, FALSE);
2824 if (hookdata->update_flags & FOLDER_ADD_FOLDERITEM)
2825 folderview_create_folder_node(folderview, hookdata->item);
2826 else if (hookdata->update_flags & FOLDER_RENAME_FOLDERITEM) {
2827 GtkCMCTreeNode *node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree),
2828 NULL, folder_item_parent(hookdata->item));
2829 folderview_sort_folders(folderview, node, hookdata->folder);
2830 } else if (hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM) {
2831 GtkCMCTreeNode *node;
2833 node = gtk_cmctree_find_by_row_data(GTK_CMCTREE(ctree), NULL, hookdata->item);
2835 gtk_cmctree_remove_node(GTK_CMCTREE(ctree), node);
2836 if (folderview->selected == node)
2837 folderview->selected = NULL;
2838 if (folderview->opened == node)
2839 folderview->opened = NULL;
2841 } else if (hookdata->update_flags & FOLDER_MOVE_FOLDERITEM) {
2842 /* do nothing, it's done by the ADD and REMOVE) */
2843 } else if (hookdata->update_flags & (FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDER | FOLDER_REMOVE_FOLDER))
2844 folderview_set(folderview);
2849 static gboolean folderview_dnd_scroll_cb(gpointer data)
2851 FolderView *folderview = (FolderView *)data;
2852 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2853 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2854 gint new_val = (int)gtk_adjustment_get_value(pos) + folderview->scroll_value;
2855 gint max = (int)gtk_adjustment_get_upper(pos) -
2856 (int)gtk_adjustment_get_page_size(pos);
2858 if (folderview->scroll_value == 0) {
2859 folderview->scroll_timeout_id = 0;
2863 if (folderview->scroll_value > 0 && new_val > max) {
2865 } else if (folderview->scroll_value < 0 && new_val < 0) {
2868 gtk_adjustment_set_value(pos, new_val);
2873 static gboolean folderview_drag_motion_cb(GtkWidget *widget,
2874 GdkDragContext *context,
2878 FolderView *folderview)
2881 FolderItem *item = NULL, *src_item = NULL;
2882 GtkCMCTreeNode *node = NULL;
2883 gboolean acceptable = FALSE;
2884 GtkAdjustment *pos = gtk_scrolled_window_get_vadjustment(
2885 GTK_SCROLLED_WINDOW(folderview->scrolledwin));
2886 int height = (int)gtk_adjustment_get_page_size(pos);
2887 int total_height = (int)gtk_adjustment_get_upper(pos);
2888 int vpos = (int)gtk_adjustment_get_value(pos);
2889 int offset = prefs_common.show_col_headers ? 24:0;
2892 if (gtk_cmclist_get_selection_info
2893 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column)) {
2894 GtkWidget *srcwidget;
2896 if (y > height - (48 - offset) && height + vpos < total_height) {
2897 dist = -(height - (48 - offset) - y);
2898 folderview->scroll_value = 1.41f * (1+(dist / 6));
2899 } else if (y < 72 - (24 - offset) && y >= 0) {
2900 dist = 72 - (24 - offset) - y;
2901 folderview->scroll_value = -1.41f * (1+(dist / 6));
2903 folderview->scroll_value = 0;
2905 if (folderview->scroll_value != 0 && folderview->scroll_timeout_id == 0) {
2906 folderview->scroll_timeout_id =
2907 g_timeout_add(30, folderview_dnd_scroll_cb,
2911 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
2912 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
2913 src_item = folderview->summaryview->folder_item;
2915 srcwidget = gtk_drag_get_source_widget(context);
2916 if (srcwidget == summary_get_main_widget(folderview->summaryview)) {
2917 /* comes from summaryview */
2918 /* we are copying messages, so only accept folder items that are not
2919 the source item, are no root items and can copy messages */
2920 if (item && item->folder && folder_item_parent(item) != NULL && src_item &&
2921 src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2922 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2924 } else if (srcwidget == folderview->ctree) {
2925 /* comes from folderview */
2926 /* we are moving folder items, only accept folders that are not
2927 the source items and can copy messages and create folder items */
2928 if (item && item->folder && src_item && src_item != item &&
2929 FOLDER_CLASS(item->folder)->copy_msg != NULL &&
2930 FOLDER_CLASS(item->folder)->create_folder != NULL &&
2931 ((FOLDER_TYPE(item->folder) != F_UNKNOWN && FOLDER_TYPE(src_item->folder) != F_UNKNOWN)
2932 || item->folder == src_item->folder))
2935 /* comes from another app */
2936 /* we are adding messages, so only accept folder items that are
2937 no root items and can copy messages */
2938 if (item && item->folder && folder_item_parent(item) != NULL
2939 && FOLDER_CLASS(item->folder)->add_msg != NULL &&
2940 FOLDER_TYPE(item->folder) != F_UNKNOWN)
2945 if (acceptable || (src_item && src_item == item))
2946 drag_state_start(folderview, node, item);
2949 g_signal_handlers_block_by_func
2951 G_CALLBACK(folderview_selected), folderview);
2952 gtk_cmctree_select(GTK_CMCTREE(widget), node);
2953 g_signal_handlers_unblock_by_func
2955 G_CALLBACK(folderview_selected), folderview);
2956 gdk_drag_status(context,
2957 (gdk_drag_context_get_actions(context) == GDK_ACTION_COPY ?
2958 GDK_ACTION_COPY : GDK_ACTION_MOVE) , time);
2960 if (folderview->opened)
2961 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2962 gdk_drag_status(context, 0, time);
2968 static void folderview_drag_leave_cb(GtkWidget *widget,
2969 GdkDragContext *context,
2971 FolderView *folderview)
2973 drag_state_stop(folderview);
2974 folderview->scroll_value = 0;
2975 gtk_cmctree_select(GTK_CMCTREE(widget), folderview->opened);
2978 static void free_info (gpointer stuff, gpointer data)
2983 void folderview_finish_dnd(const gchar *data, GdkDragContext *drag_context,
2984 guint time, FolderItem *item)
2987 GSList *msglist = NULL;
2988 list = uri_list_extract_filenames(data);
2989 if (!(item && item->folder && folder_item_parent(item) != NULL
2990 && FOLDER_CLASS(item->folder)->add_msg != NULL))
2992 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2993 debug_print("item doesn't fit\n");
2997 gtk_drag_finish(drag_context, FALSE, FALSE, time);
2998 debug_print("list is empty\n");
3001 for (tmp = list; tmp != NULL; tmp = tmp->next) {
3002 MsgFileInfo *info = NULL;
3004 if (file_is_email((gchar *)tmp->data)) {
3005 info = g_new0(MsgFileInfo, 1);
3006 info->msginfo = NULL;
3007 info->file = (gchar *)tmp->data;
3008 msglist = g_slist_prepend(msglist, info);
3009 debug_print("file is a mail\n");
3011 debug_print("file isn't a mail\n");
3015 msglist = g_slist_reverse(msglist);
3016 folder_item_add_msgs(item, msglist, FALSE);
3017 g_slist_foreach(msglist, free_info, NULL);
3018 g_slist_free(msglist);
3019 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3021 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3023 list_free_strings(list);
3027 static void folderview_drag_received_cb(GtkWidget *widget,
3028 GdkDragContext *drag_context,
3031 GtkSelectionData *data,
3034 FolderView *folderview)
3037 FolderItem *item = NULL, *src_item;
3038 GtkCMCTreeNode *node;
3039 int offset = prefs_common.show_col_headers ? 24:0;
3041 folderview->scroll_value = 0;
3043 if (info == TARGET_DUMMY) {
3044 drag_state_stop(folderview);
3045 const gchar *ddata = (const gchar *)gtk_selection_data_get_data(data);
3046 if ((gchar *)strstr(ddata, "FROM_OTHER_FOLDER") != ddata) {
3047 /* comes from summaryview */
3048 if (gtk_cmclist_get_selection_info
3049 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3052 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3053 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3054 src_item = folderview->summaryview->folder_item;
3056 if (item->no_select) {
3057 alertpanel_error(_("The destination folder can only be used to "
3058 "store subfolders."));
3061 /* re-check (due to acceptable possibly set for folder moves */
3062 if (!(item && item->folder && item->path && !item->no_select &&
3063 src_item && src_item != item && FOLDER_CLASS(item->folder)->copy_msg != NULL)) {
3066 if (item && src_item) {
3067 switch (gdk_drag_context_get_selected_action(drag_context)) {
3068 case GDK_ACTION_COPY:
3069 summary_copy_selected_to(folderview->summaryview, item);
3070 gtk_drag_finish(drag_context, TRUE, FALSE, time);
3072 case GDK_ACTION_MOVE:
3073 case GDK_ACTION_DEFAULT:
3075 if (FOLDER_CLASS(src_item->folder)->remove_msg == NULL)
3076 summary_copy_selected_to(folderview->summaryview, item);
3078 summary_move_selected_to(folderview->summaryview, item);
3079 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3082 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3084 /* comes from folderview */
3086 gboolean folder_is_normal = TRUE;
3087 gboolean copy = (GDK_ACTION_COPY ==
3088 gdk_drag_context_get_selected_action(drag_context));
3090 source = (char *)gtk_selection_data_get_data(data) + 17;
3091 if (gtk_cmclist_get_selection_info
3092 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0
3094 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3097 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3098 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3099 src_item = folder_find_item_from_identifier(source);
3103 src_item->stype == F_NORMAL &&
3104 !folder_has_parent_of_type(src_item, F_OUTBOX) &&
3105 !folder_has_parent_of_type(src_item, F_DRAFT) &&
3106 !folder_has_parent_of_type(src_item, F_QUEUE) &&
3107 !folder_has_parent_of_type(src_item, F_TRASH);
3108 if (!item || !src_item || !folder_is_normal) {
3109 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3113 folderview_move_folder(folderview, src_item, item, copy);
3114 gtk_drag_finish(drag_context, TRUE, TRUE, time);
3116 folderview->nodes_to_recollapse = NULL;
3117 } else if (info == TARGET_MAIL_URI_LIST) {
3118 if (gtk_cmclist_get_selection_info
3119 (GTK_CMCLIST(widget), x - offset, y - offset, &row, &column) == 0)
3122 node = gtk_cmctree_node_nth(GTK_CMCTREE(widget), row);
3124 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3125 debug_print("no node\n");
3128 item = gtk_cmctree_node_get_row_data(GTK_CMCTREE(widget), node);
3130 gtk_drag_finish(drag_context, FALSE, FALSE, time);
3131 debug_print("no item\n");
3134 folderview_finish_dnd(gtk_selection_data_get_data(data),
3135 drag_context, time, item);
3139 static void folderview_drag_end_cb(GtkWidget *widget,
3140 GdkDragContext *drag_context,
3141 FolderView *folderview)
3143 drag_state_stop(folderview);
3144 folderview->scroll_value = 0;
3145 g_slist_free(folderview->nodes_to_recollapse);
3146 folderview->nodes_to_recollapse = NULL;
3149 void folderview_register_popup(FolderViewPopup *fpopup)
3153 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3154 FolderView *folderview = folderviews->data;
3155 GtkActionGroup *factory;
3157 factory = create_action_group(folderview, fpopup);
3158 g_hash_table_insert(folderview->popups, fpopup->klass, factory);
3160 g_hash_table_insert(folderview_popups, fpopup->klass, fpopup);
3163 void folderview_unregister_popup(FolderViewPopup *fpopup)
3168 for (folderviews = folderview_list; folderviews != NULL; folderviews = g_list_next(folderviews)) {
3169 FolderView *folderview = folderviews->data;
3171 g_hash_table_remove(folderview->popups, fpopup->klass);
3173 g_hash_table_remove(folderview_popups, fpopup->klass);